新增 Http Digest 认证模块简单实现

This commit is contained in:
click33 2024-04-18 14:39:12 +08:00
parent ccb79f6494
commit 543613b5dd
10 changed files with 963 additions and 3 deletions

View File

@ -0,0 +1,61 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.annotation;
import cn.dev33.satoken.httpauth.digest.SaHttpDigestModel;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Http Digest 认证校验只有通过 Http Digest 认证后才能进入该方法否则抛出异常
*
* <p> 可标注在方法类上效果等同于标注在此类的所有方法上
*
* @author click33
* @since 1.38.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface SaCheckHttpDigest {
/**
* 用户名
* @return /
*/
String username() default "";
/**
* 密码
* @return /
*/
String password() default "";
/**
* 领域
* @return /
*/
String realm() default SaHttpDigestModel.DEFAULT_REALM;
/**
* 需要校验的用户名和密码格式形如 sa:123456
* @return /
*/
String value() default "";
}

View File

@ -148,10 +148,15 @@ public class SaTokenConfig implements Serializable {
private String jwtSecretKey;
/**
* Http Basic 认证的默认账号和密码
* Http Basic 认证的默认账号和密码冒号隔开例如sa:123456
*/
private String basic = "";
/**
* Http Digest 认证的默认账号和密码冒号隔开例如sa:123456
*/
private String httpDigest = "";
/**
* 配置当前项目的网络访问地址
*/
@ -570,6 +575,22 @@ public class SaTokenConfig implements Serializable {
return this;
}
/**
* @return Http Digest 认证的默认账号和密码冒号隔开例如sa:123456
*/
public String getHttpDigest() {
return httpDigest;
}
/**
* @param httpDigest Http Digest 认证的默认账号和密码冒号隔开例如sa:123456
* @return 对象自身
*/
public SaTokenConfig setHttpDigest(String httpDigest) {
this.httpDigest = httpDigest;
return this;
}
/**
* @return 配置当前项目的网络访问地址
*/
@ -676,7 +697,8 @@ public class SaTokenConfig implements Serializable {
+ ", logLevelInt=" + logLevelInt
+ ", isColorLog=" + isColorLog
+ ", jwtSecretKey=" + jwtSecretKey
+ ", basic=" + basic
+ ", basic=" + basic
+ ", httpDigest=" + httpDigest
+ ", currDomain=" + currDomain
+ ", sameTokenTimeout=" + sameTokenTimeout
+ ", checkSameToken=" + checkSameToken

View File

@ -60,6 +60,9 @@ public interface SaErrorCode {
/** 表示未能通过 Http Basic 认证校验 */
int CODE_10311 = 10311;
/** 表示未能通过 Http Digest 认证校验 */
int CODE_10312 = 10312;
/** 提供的 HttpMethod 是无效的 */
int CODE_10321 = 10321;

View File

@ -0,0 +1,41 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.exception;
/**
* 一个异常代表会话未能通过 Http Digest 认证校验
*
* @author click33
* @since 1.38.0
*/
public class NotHttpDigestAuthException extends SaTokenException {
/**
* 序列化版本号
*/
private static final long serialVersionUID = 6806129545290130144L;
/** 异常提示语 */
public static final String BE_MESSAGE = "no http digest auth";
/**
* 一个异常代表会话未通过 Http Digest 认证
*/
public NotHttpDigestAuthException() {
super(BE_MESSAGE);
}
}

View File

@ -105,7 +105,16 @@ public class SaTokenException extends RuntimeException {
this.code = code;
return this;
}
/**
* 断言 flag 不为 true否则抛出 message 异常
* @param flag 标记
* @param message 异常信息
*/
public static void notTrue(boolean flag, String message) {
notTrue(flag, message, SaErrorCode.CODE_UNDEFINED);
}
/**
* 断言 flag 不为 true否则抛出 message 异常
* @param flag 标记
@ -118,6 +127,15 @@ public class SaTokenException extends RuntimeException {
}
}
/**
* 断言 value 不为空否则抛出 message 异常
* @param value
* @param message 异常信息
*/
public static void notEmpty(Object value, String message) {
notEmpty(value, message, SaErrorCode.CODE_UNDEFINED);
}
/**
* 断言 value 不为空否则抛出 message 异常
* @param value

View File

@ -0,0 +1,346 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.httpauth.digest;
/**
* Sa-Token Http Digest 认证 - 参数实体类
*
* @author click33
* @since 1.38.0
*/
public class SaHttpDigestModel {
/**
* 默认的 Realm 领域名称
*/
public static final String DEFAULT_REALM = "Sa-Token";
/**
* 默认的 qop
*/
public static final String DEFAULT_QOP = "auth";
/**
* 用户名
*/
public String username;
/**
* 密码
*/
public String password;
/**
* 领域
*/
public String realm = DEFAULT_REALM;
/**
* 随机数
*/
public String nonce;
/**
* 请求 uri
*/
public String uri;
/**
* 请求方法
*/
public String method;
/**
* 保护质量auth=默认的auth-int=增加报文完整性检测可以为空但不推荐
*/
public String qop;
/**
* nonce计数器是一个16进制的数值表示同一nonce下客户端发送出请求的数量
*/
public String nc;
/**
* 客户端随机数由客户端提供
*/
public String cnonce;
/**
* opaque
*/
public String opaque;
/**
* 请求摘要最终计算的摘要结果
*/
public String response;
// ------------------- 构造函数 -------------------
public SaHttpDigestModel() {
}
public SaHttpDigestModel(String username, String password) {
this.username = username;
this.password = password;
}
public SaHttpDigestModel(String username, String password, String realm) {
this.username = username;
this.password = password;
this.realm = realm;
}
// ------------------- get/set -------------------
/**
* 获取 用户名
*
* @return username 用户名
*/
public String getUsername() {
return this.username;
}
/**
* 设置 用户名
*
* @param username 用户名
* @return /
*/
public SaHttpDigestModel setUsername(String username) {
this.username = username;
return this;
}
/**
* 获取 领域
*
* @return realm 领域
*/
public String getRealm() {
return this.realm;
}
/**
* 设置 领域
*
* @param realm 领域
* @return /
*/
public SaHttpDigestModel setRealm(String realm) {
this.realm = realm;
return this;
}
/**
* 获取 密码
*
* @return password 密码
*/
public String getPassword() {
return this.password;
}
/**
* 设置 密码
*
* @param password 密码
* @return /
*/
public SaHttpDigestModel setPassword(String password) {
this.password = password;
return this;
}
/**
* 获取 随机数
*
* @return nonce 随机数
*/
public String getNonce() {
return this.nonce;
}
/**
* 设置 随机数
*
* @param nonce 随机数
* @return /
*/
public SaHttpDigestModel setNonce(String nonce) {
this.nonce = nonce;
return this;
}
/**
* 获取 请求 uri
*
* @return uri 请求 uri
*/
public String getUri() {
return this.uri;
}
/**
* 设置 请求 uri
*
* @param uri 请求 uri
* @return /
*/
public SaHttpDigestModel setUri(String uri) {
this.uri = uri;
return this;
}
/**
* 获取 请求方法
*
* @return method 请求方法
*/
public String getMethod() {
return this.method;
}
/**
* 设置 请求方法
*
* @param method 请求方法
* @return /
*/
public SaHttpDigestModel setMethod(String method) {
this.method = method;
return this;
}
/**
* 获取 保护质量auth=默认的auth-int=增加报文完整性检测可以为空但不推荐
*
* @return qop 保护质量auth=默认的auth-int=增加报文完整性检测可以为空但不推荐
*/
public String getQop() {
return this.qop;
}
/**
* 设置 保护质量auth=默认的auth-int=增加报文完整性检测可以为空但不推荐
*
* @param qop 保护质量auth=默认的auth-int=增加报文完整性检测可以为空但不推荐
* @return /
*/
public SaHttpDigestModel setQop(String qop) {
this.qop = qop;
return this;
}
/**
* 获取 nonce计数器是一个16进制的数值表示同一nonce下客户端发送出请求的数量
*
* @return nc nonce计数器是一个16进制的数值表示同一nonce下客户端发送出请求的数量
*/
public String getNc() {
return this.nc;
}
/**
* 设置 nonce计数器是一个16进制的数值表示同一nonce下客户端发送出请求的数量
*
* @param nc nonce计数器是一个16进制的数值表示同一nonce下客户端发送出请求的数量
* @return /
*/
public SaHttpDigestModel setNc(String nc) {
this.nc = nc;
return this;
}
/**
* 获取 客户端随机数由客户端提供
*
* @return cnonce 客户端随机数由客户端提供
*/
public String getCnonce() {
return this.cnonce;
}
/**
* 设置 客户端随机数由客户端提供
*
* @param cnonce 客户端随机数由客户端提供
* @return /
*/
public SaHttpDigestModel setCnonce(String cnonce) {
this.cnonce = cnonce;
return this;
}
/**
* 获取 opaque
*
* @return opaque opaque
*/
public String getOpaque() {
return this.opaque;
}
/**
* 设置 opaque
*
* @param opaque opaque
* @return /
*/
public SaHttpDigestModel setOpaque(String opaque) {
this.opaque = opaque;
return this;
}
/**
* 获取 请求摘要最终计算的摘要结果
*
* @return response 请求摘要最终计算的摘要结果
*/
public String getResponse() {
return this.response;
}
/**
* 设置 请求摘要最终计算的摘要结果
*
* @param response 请求摘要最终计算的摘要结果
* @return /
*/
public SaHttpDigestModel setResponse(String response) {
this.response = response;
return this;
}
@Override
public String toString() {
return "SaHttpDigestModel[" +
"username=" + username +
", password=" + password +
", realm=" + realm +
", nonce=" + nonce +
", uri=" + uri +
", method=" + method +
", qop=" + qop +
", nc=" + nc +
", cnonce=" + cnonce +
", opaque=" + opaque +
", response=" + response +
"]";
}
}

View File

@ -0,0 +1,295 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.httpauth.digest;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.annotation.SaCheckHttpDigest;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.error.SaErrorCode;
import cn.dev33.satoken.exception.NotHttpDigestAuthException;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.util.SaFoxUtil;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Sa-Token Http Digest 认证模块 - 模板方法类
*
* @author click33
* @since 1.38.0
*/
public class SaHttpDigestTemplate {
/*
这里只是 Http Digest 认证的一个简单实现待实现功能还有
1nonce 防重放攻击
2nc 计数器
3qop 保护质量=auth-int
4opaque 透明值
5algorithm 更多摘要算法
等等
*/
/**
* 构建认证失败的响应头参数
* @param model 参数对象
* @return 响应头值
*/
public String buildResponseHeaderValue(SaHttpDigestModel model) {
// 抛异常
String headerValue = "Digest " +
"realm=\"" + model.realm + "\", " +
"qop=\"" + model.qop + "\", " +
"nonce=\"" + model.nonce + "\", " +
"nc=" + model.nc + ", " +
"opaque=\"" + model.opaque + "\"";
return headerValue;
}
/**
* 在校验失败时设置响应头并抛出异常
* @param model Digest 参数对象
*/
public void throwNotHttpDigestAuthException(SaHttpDigestModel model) {
// 补全一些必须的参数
model.realm = (model.realm != null) ? model.realm : SaHttpDigestModel.DEFAULT_REALM;
model.qop = (model.qop != null) ? model.qop : SaHttpDigestModel.DEFAULT_QOP;
model.nonce = (model.nonce != null) ? model.nonce : SaFoxUtil.getRandomString(32);
model.opaque = (model.opaque != null) ? model.opaque : SaFoxUtil.getRandomString(32);
model.nc = (model.nc != null) ? model.nc : "00000001";
// 设置响应头
SaHolder.getResponse()
.setStatus(401)
.setHeader("WWW-Authenticate", buildResponseHeaderValue(model));
// 抛异常
throw new NotHttpDigestAuthException().setCode(SaErrorCode.CODE_10312);
}
/**
* 获取浏览器提交的 Digest 参数 裁剪掉前缀
* @return
*/
public String getAuthorizationValue() {
// 获取前端提交的请求头 Authorization 参数
String authorization = SaHolder.getRequest().getHeader("Authorization");
// 如果不是以 Digest 作为前缀则视为无效
if(authorization == null || ! authorization.startsWith("Digest ")) {
return null;
}
// 裁剪前缀并解码
return authorization.substring(7);
}
/**
* 获取浏览器提交的 Digest 参数并转化为 Map
* @return /
*/
public SaHttpDigestModel getAuthorizationValueToModel() {
// 先获取字符串值
String authorization = getAuthorizationValue();
if(authorization == null) {
// throw new SaTokenException("请求头中未携带 Digest 认证参数");
return null;
}
// 根据逗号分割解析为 Map
Map<String, String> map = new LinkedHashMap<>();
String[] arr = authorization.split(",");
for (String s : arr) {
String[] kv = s.split("=");
if (kv.length == 2) {
map.put(kv[0].trim(), kv[1].trim().replace("\"", ""));
}
}
/*
参考样例
username=sa,
realm=Sa-Token,
nonce=dcd98b7102dd2f0e8b11d0f600bfb0c093,
uri=/test/testDigest,
response=a32023c128e142163dd4856a2f511c70,
opaque=5ccc069c403ebaf9f0171e9517f40e41,
qop=auth,
nc=00000002,
cnonce=f3ca6bfc0b2f59c4
*/
// 转化为 Model
SaHttpDigestModel model = new SaHttpDigestModel();
model.username = map.get("username");
model.realm = map.get("realm");
model.nonce = map.get("nonce");
model.uri = map.get("uri");
model.method = SaHolder.getRequest().getMethod();
model.qop = map.get("qop");
model.nc = map.get("nc");
model.cnonce = map.get("cnonce");
model.opaque = map.get("opaque");
model.response = map.get("response");
//
return model;
}
/**
* 计算根据 Digest 参数计算 response
*
* @param model Digest 参数对象
* @return 计算出的 response
*/
public String calcResponse(SaHttpDigestModel model) {
// frag1 = md5(username:realm:password)
String frag1 = SaSecureUtil.md5(model.username + ":" + model.realm + ":" + model.password);
// frag2 = nonce:nc:cnonce:qop
String frag2 = model.nonce + ":" + model.nc + ":" + model.cnonce + ":" + model.qop;
// frag3 = md5(method:uri)
String frag3 = SaSecureUtil.md5(model.method + ":" + model.uri);
// 最终结果 = md5(frag1:frag2:frag3)
String response = SaSecureUtil.md5(frag1 + ":" + frag2 + ":" + frag3);
//
return response;
}
/**
* hopeModel 有的值都 copy reqModel
*/
public void copyHopeToReq(SaHttpDigestModel hopeModel, SaHttpDigestModel reqModel){
reqModel.username = hopeModel.username;
reqModel.password = hopeModel.password;
reqModel.realm = hopeModel.realm != null ? hopeModel.realm : reqModel.realm;
reqModel.nonce = hopeModel.nonce != null ? hopeModel.nonce : reqModel.nonce;
reqModel.uri = hopeModel.uri != null ? hopeModel.uri : reqModel.uri;
reqModel.method = hopeModel.method != null ? hopeModel.method : reqModel.method;
reqModel.qop = hopeModel.qop != null ? hopeModel.qop : reqModel.qop;
reqModel.nc = hopeModel.nc != null ? hopeModel.nc : reqModel.nc;
reqModel.opaque = hopeModel.opaque != null ? hopeModel.opaque : reqModel.opaque;
// reqModel.cnonce = hopeModel.cnonce != null ? hopeModel.cnonce : reqModel.cnonce;
// reqModel.response = hopeModel.response != null ? hopeModel.response : reqModel.response;
}
// ---------- 校验 ----------
/**
* 校验根据提供 Digest 参数计算 res request 请求中的 Digest 参数进行校验校验不通过则抛出异常
* @param hopeModel 提供的 Digest 参数对象
*/
public void check(SaHttpDigestModel hopeModel) {
// 先进行一些必须的希望参数校验
SaTokenException.notEmpty(hopeModel, "Digest参数对象不能为空");
SaTokenException.notEmpty(hopeModel.username, "必须提供希望的 username 参数");
SaTokenException.notEmpty(hopeModel.password, "必须提供希望的 password 参数");
// 获取 web 请求中的 Digest 参数
SaHttpDigestModel reqModel = getAuthorizationValueToModel();
// 为空代表前端根本没有提交 Digest 参数直接抛异常
if(reqModel == null) {
throwNotHttpDigestAuthException(hopeModel);
}
// hopeModel 有的值都 copy reqModel
copyHopeToReq(hopeModel, reqModel);
// 计算
String cResponse = calcResponse(reqModel);
// 比对不一致就抛异常
if(! cResponse.equals(reqModel.response)) {
throwNotHttpDigestAuthException(hopeModel);
}
// 认证通过
}
/**
* 校验根据提供的参数校验不通过抛出异常
* @param username 用户名
* @param password 密码
*/
public void check(String username, String password) {
check(new SaHttpDigestModel(username, password));
}
/**
* 校验根据提供的参数校验不通过抛出异常
* @param username 用户名
* @param password 密码
* @param realm 领域
*/
public void check(String username, String password, String realm) {
check(new SaHttpDigestModel(username, password, realm));
}
/**
* 校验根据全局配置参数校验不通过抛出异常
*/
public void check() {
String httpDigest = SaManager.getConfig().getHttpDigest();
if(SaFoxUtil.isEmpty(httpDigest)){
throw new SaTokenException("未配置全局 Http Digest 认证参数");
}
String[] arr = httpDigest.split(":");
if(arr.length != 2){
throw new SaTokenException("全局 Http Digest 认证参数配置错误格式应如username:password");
}
check(arr[0], arr[1]);
}
/**
* 根据注解 ( @SaCheckHttpDigest ) 鉴权
*
* @param at 注解对象
*/
public void checkByAnnotation(SaCheckHttpDigest at) {
// 如果配置了 value则以 value 优先
String value = at.value();
if(SaFoxUtil.isNotEmpty(value)){
String[] arr = value.split(":");
if(arr.length != 2){
throw new SaTokenException("注解参数配置错误格式应如username:password");
}
check(arr[0], arr[1]);
return;
}
// 如果配置了 username则分别获取参数
String username = at.username();
if(SaFoxUtil.isNotEmpty(username)){
check(username, at.password(), at.realm());
return;
}
// 都没有配置则根据全局配置参数进行校验
check();
}
}

View File

@ -0,0 +1,157 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.httpauth.digest;
import cn.dev33.satoken.annotation.SaCheckHttpDigest;
import cn.dev33.satoken.context.SaHolder;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Sa-Token Http Digest 认证模块Util 工具类
*
* @author click33
* @since 1.38.0
*/
public class SaHttpDigestUtil {
private SaHttpDigestUtil() {
}
/**
* 底层使用的 SaHttpDigestTemplate 对象
*/
public static SaHttpDigestTemplate saHttpDigestTemplate = new SaHttpDigestTemplate();
/**
* 获取浏览器提交的 Digest 参数 裁剪掉前缀
* @return
*/
public static String getAuthorizationValue() {
// 获取前端提交的请求头 Authorization 参数
String authorization = SaHolder.getRequest().getHeader("Authorization");
// 如果不是以 Digest 作为前缀则视为无效
if(authorization == null || ! authorization.startsWith("Digest ")) {
return null;
}
// 裁剪前缀并解码
return authorization.substring(7);
}
/**
* 获取浏览器提交的 Digest 参数并转化为 Map
* @return /
*/
public static SaHttpDigestModel getAuthorizationValueToModel() {
// 先获取字符串值
String authorization = getAuthorizationValue();
if(authorization == null) {
// throw new SaTokenException("请求头中未携带 Digest 认证参数");
return null;
}
// 根据逗号分割解析为 Map
Map<String, String> map = new LinkedHashMap<>();
String[] arr = authorization.split(",");
for (String s : arr) {
String[] kv = s.split("=");
if (kv.length == 2) {
map.put(kv[0].trim(), kv[1].trim().replace("\"", ""));
}
}
/*
参考样例
username=sa,
realm=Sa-Token,
nonce=dcd98b7102dd2f0e8b11d0f600bfb0c093,
uri=/test/testDigest,
response=a32023c128e142163dd4856a2f511c70,
opaque=5ccc069c403ebaf9f0171e9517f40e41,
qop=auth,
nc=00000002,
cnonce=f3ca6bfc0b2f59c4
*/
// 转化为 Model
SaHttpDigestModel model = new SaHttpDigestModel();
model.username = map.get("username");
model.realm = map.get("realm");
model.nonce = map.get("nonce");
model.uri = map.get("uri");
model.method = SaHolder.getRequest().getMethod();
model.qop = map.get("qop");
model.nc = map.get("nc");
model.cnonce = map.get("cnonce");
model.opaque = map.get("opaque");
model.response = map.get("response");
//
return model;
}
// ---------- 校验 ----------
/**
* 校验根据提供 Digest 参数计算 res request 请求中的 Digest 参数进行校验校验不通过则抛出异常
* @param hopeModel 提供的 Digest 参数对象
*/
public static void check(SaHttpDigestModel hopeModel) {
saHttpDigestTemplate.check(hopeModel);
}
/**
* 校验根据提供的参数校验不通过抛出异常
* @param username 用户名
* @param password 密码
*/
public static void check(String username, String password) {
saHttpDigestTemplate.check(username, password);
}
/**
* 校验根据提供的参数校验不通过抛出异常
* @param username 用户名
* @param password 密码
* @param realm 领域
*/
public static void check(String username, String password, String realm) {
saHttpDigestTemplate.check(username, password, realm);
}
/**
* 校验根据全局配置参数校验不通过抛出异常
*/
public static void check() {
saHttpDigestTemplate.check();
}
/**
* 根据注解 ( @SaCheckHttpDigest ) 鉴权
*
* @param at 注解对象
*/
public static void checkByAnnotation(SaCheckHttpDigest at) {
saHttpDigestTemplate.checkByAnnotation(at);
}
}

View File

@ -21,6 +21,7 @@ import cn.dev33.satoken.basic.SaBasicUtil;
import cn.dev33.satoken.exception.RequestPathInvalidException;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.fun.strategy.*;
import cn.dev33.satoken.httpauth.digest.SaHttpDigestUtil;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.util.SaFoxUtil;
@ -185,6 +186,12 @@ public final class SaStrategy {
SaBasicUtil.check(checkBasic.realm(), checkBasic.account());
}
// 校验 @SaCheckBasic 注解
SaCheckHttpDigest checkHttpDigest = (SaCheckHttpDigest) SaStrategy.instance.getAnnotation.apply(element, SaCheckHttpDigest.class);
if(checkHttpDigest != null) {
SaHttpDigestUtil.checkByAnnotation(checkHttpDigest);
}
// 校验 @SaCheckOr 注解
SaCheckOr checkOr = (SaCheckOr) SaStrategy.instance.getAnnotation.apply(element, SaCheckOr.class);
if(checkOr != null) {

View File

@ -1,5 +1,6 @@
package com.pj.test;
import cn.dev33.satoken.annotation.SaCheckHttpDigest;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.spring.SpringMVCUtil;
import cn.dev33.satoken.stp.SaLoginConfig;
@ -52,4 +53,13 @@ public class TestController {
return SaResult.ok();
}
// 测试 Http Digest 认证 浏览器访问 http://localhost:8081/test/testDigest
@SaCheckHttpDigest("sa:123456")
@RequestMapping("testDigest")
public SaResult testDigest() {
// SaHttpDigestUtil.check("sa", "123456");
// 返回
return SaResult.data(null);
}
}