新增 Http Basic 认证

This commit is contained in:
click33 2021-08-31 00:11:00 +08:00
parent 3567e9f54b
commit cc4cc89e27
19 changed files with 366 additions and 9 deletions

View File

@ -6,10 +6,12 @@ import java.util.List;
import java.util.UUID;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.annotation.SaCheckBasic;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaCheckRole;
import cn.dev33.satoken.annotation.SaCheckSafe;
import cn.dev33.satoken.basic.SaBasicUtil;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.dev33.satoken.util.SaTokenConsts;
@ -133,6 +135,12 @@ public class SaTokenActionDefaultImpl implements SaTokenAction {
SaCheckSafe at = target.getAnnotation(SaCheckSafe.class);
SaManager.getStpLogic(null).checkByAnnotation(at);
}
// 校验 @SaCheckBasic 注解
if(target.isAnnotationPresent(SaCheckBasic.class)) {
SaCheckBasic at = target.getAnnotation(SaCheckBasic.class);
SaBasicUtil.check(at.realm(), at.account());
}
}
}

View File

@ -0,0 +1,32 @@
package cn.dev33.satoken.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import cn.dev33.satoken.basic.SaBasicTemplate;
/**
* Http Basic 认证只有通过 Basic 认证才能进入该方法
* <p> 可标注在函数类上效果等同于标注在此类的所有方法上
* @author kong
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface SaCheckBasic {
/**
* 领域
* @return see note
*/
String realm() default SaBasicTemplate.DEFAULT_REALM;
/**
* 需要校验的账号密码
* @return see note
*/
String account() default "";
}

View File

@ -0,0 +1,79 @@
package cn.dev33.satoken.basic;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.exception.NotBasicAuthException;
import cn.dev33.satoken.secure.SaBase64Util;
import cn.dev33.satoken.util.SaFoxUtil;
/**
* Sa-Token Http Basic 认证模块
* @author kong
*
*/
public class SaBasicTemplate {
/**
* 默认的 Realm 名称
*/
public static final String DEFAULT_REALM = "Sa-Token";
/**
* 设置响应头并抛出异常
* @param realm 领域
*/
public void throwNotBasicAuthException(String realm) {
SaHolder.getResponse().setStatus(401).setHeader("WWW-Authenticate", "Basic Realm=" + realm);
throw new NotBasicAuthException();
}
/**
* 获取浏览器提交的 Basic 参数 裁剪掉前缀并解码
* @return
*/
public String getAuthorizationValue() {
// 获取请求头 Authorization 参数
String authorization = SaHolder.getRequest().getHeader("Authorization");
// 如果不是以 Basic 作为前缀则视为无效
if(authorization == null || authorization.startsWith("Basic ") == false) {
return null;
}
// 裁剪前缀并解码
return SaBase64Util.decode(authorization.substring(6));
}
/**
* 对当前会话进行 Basic 校验使用全局配置的账号密码校验不通过则抛出异常
* @param basicAccount 账号格式为 user:password
*/
public void check() {
check(DEFAULT_REALM, SaManager.getConfig().getBasic());
}
/**
* 对当前会话进行 Basic 校验手动设置账号密码校验不通过则抛出异常
* @param account 账号格式为 user:password
*/
public void check(String account) {
check(DEFAULT_REALM, account);
}
/**
* 对当前会话进行 Basic 校验手动设置 Realm & 账号密码校验不通过则抛出异常
* @param realm 领域
* @param account 账号格式为 user:password
*/
public void check(String realm, String account) {
if(SaFoxUtil.isEmpty(account)) {
account = SaManager.getConfig().getBasic();
}
String authorization = getAuthorizationValue();
if(SaFoxUtil.isEmpty(authorization) || authorization.equals(account) == false) {
throwNotBasicAuthException(realm);
}
}
}

View File

@ -0,0 +1,48 @@
package cn.dev33.satoken.basic;
/**
* Sa-Token Http Basic 认证 Util
* @author kong
*
*/
public class SaBasicUtil {
/**
* 底层 SaBasicTemplate 对象
*/
public static SaBasicTemplate saBasicTemplate = new SaBasicTemplate();
/**
* 获取浏览器提交的 Basic 参数 裁剪掉前缀并解码
* @return
*/
public static String getAuthorizationValue() {
return saBasicTemplate.getAuthorizationValue();
}
/**
* 对当前会话进行 Basic 校验使用全局配置的账号密码校验不通过则抛出异常
* @param basicAccount 账号格式为 user:password
*/
public static void check() {
saBasicTemplate.check();
}
/**
* 对当前会话进行 Basic 校验手动设置账号密码校验不通过则抛出异常
* @param account 账号格式为 user:password
*/
public static void check(String account) {
saBasicTemplate.check(account);
}
/**
* 对当前会话进行 Basic 校验手动设置 Realm & 账号密码校验不通过则抛出异常
* @param realm 领域
* @param account 账号格式为 user:password
*/
public static void check(String realm, String account) {
saBasicTemplate.check(realm, account);
}
}

View File

@ -74,6 +74,11 @@ public class SaTokenConfig implements Serializable {
* Id-Token的有效期 (单位: )
*/
private long idTokenTimeout = 60 * 60 * 24;
/**
* Http Basic 认证的账号&密码
*/
private String basic = "";
/**
@ -373,6 +378,22 @@ public class SaTokenConfig implements Serializable {
this.idTokenTimeout = idTokenTimeout;
return this;
}
/**
* @return Http Basic 认证的账号&密码
*/
public String getBasic() {
return basic;
}
/**
* @param basic Http Basic 认证的账号&密码
* @return 对象自身
*/
public SaTokenConfig setBasic(String basic) {
this.basic = basic;
return this;
}
/**
* @return SSO单点登录配置对象
@ -388,6 +409,7 @@ public class SaTokenConfig implements Serializable {
this.sso = sso;
}
@Override
public String toString() {
return "SaTokenConfig [tokenName=" + tokenName + ", timeout=" + timeout + ", activityTimeout=" + activityTimeout
@ -396,12 +418,9 @@ public class SaTokenConfig implements Serializable {
+ ", dataRefreshPeriod=" + dataRefreshPeriod + ", tokenSessionCheckLogin=" + tokenSessionCheckLogin
+ ", autoRenew=" + autoRenew + ", cookieDomain=" + cookieDomain + ", tokenPrefix=" + tokenPrefix
+ ", isPrint=" + isPrint + ", isLog=" + isLog + ", jwtSecretKey=" + jwtSecretKey + ", idTokenTimeout="
+ idTokenTimeout + ", sso=" + sso + "]";
+ idTokenTimeout + ", basic=" + basic + ", sso=" + sso + "]";
}
/**
* <h1> 本函数设计已过时未来版本可能移除此函数请及时更换为 setIsConcurrent() 使用方式保持不变 </h1>
* @param allowConcurrentLogin see note

View File

@ -28,6 +28,13 @@ public interface SaResponse {
* @param timeout 过期时间
*/
public void addCookie(String name, String value, String path, String domain, int timeout);
/**
* 设置响应状态码
* @param sc 响应状态码
* @return 对象自身
*/
public SaResponse setStatus(int sc);
/**
* 在响应头里写入一个值

View File

@ -0,0 +1,24 @@
package cn.dev33.satoken.exception;
/**
* 一个异常代表会话未能通过 Http Basic 认证
* @author kong
*/
public class NotBasicAuthException extends SaTokenException {
/**
* 序列化版本号
*/
private static final long serialVersionUID = 6806129545290130144L;
/** 异常提示语 */
public static final String BE_MESSAGE = "no basic auth";
/**
* 一个异常代表会话未通过 Http Basic 认证
*/
public NotBasicAuthException() {
super(BE_MESSAGE);
}
}

View File

@ -34,7 +34,8 @@ public class SaCheckAspect {
"@within(cn.dev33.satoken.annotation.SaCheckLogin) || @annotation(cn.dev33.satoken.annotation.SaCheckLogin) || "
+ "@within(cn.dev33.satoken.annotation.SaCheckRole) || @annotation(cn.dev33.satoken.annotation.SaCheckRole) || "
+ "@within(cn.dev33.satoken.annotation.SaCheckPermission) || @annotation(cn.dev33.satoken.annotation.SaCheckPermission) || "
+ "@within(cn.dev33.satoken.annotation.SaCheckSafe) || @annotation(cn.dev33.satoken.annotation.SaCheckSafe)";
+ "@within(cn.dev33.satoken.annotation.SaCheckSafe) || @annotation(cn.dev33.satoken.annotation.SaCheckSafe) || "
+ "@within(cn.dev33.satoken.annotation.SaCheckBasic) || @annotation(cn.dev33.satoken.annotation.SaCheckBasic)";
/**
* 声明AOP签名

View File

@ -27,23 +27,31 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.0.0.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<!-- spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.4.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<!-- reactor-core -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.1.4.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<!-- OAuth2.0 (optional) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-oauth2</artifactId>
<version>${sa-token-version}</version>
<optional>true</optional>
</dependency>
<!-- config -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>

View File

@ -74,6 +74,15 @@ public class SaResponseForReactor implements SaResponse {
response.addCookie(builder.build());
}
/**
* 设置响应状态码
*/
@Override
public SaResponse setStatus(int sc) {
response.setStatusCode(HttpStatus.valueOf(sc));
return this;
}
/**
* 在响应头里写入一个值
*/

View File

@ -6,6 +6,8 @@ import org.springframework.util.PathMatcher;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.action.SaTokenAction;
import cn.dev33.satoken.basic.SaBasicTemplate;
import cn.dev33.satoken.basic.SaBasicUtil;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.context.SaTokenContext;
import cn.dev33.satoken.dao.SaTokenDao;
@ -105,6 +107,16 @@ public class SaBeanInject {
SaIdUtil.saIdTemplate = saIdTemplate;
}
/**
* 注入 Sa-Token Http Basic 认证模块
*
* @param saBasicTemplate saBasicTemplate对象
*/
@Autowired(required = false)
public void setSaSsoTemplate(SaBasicTemplate saBasicTemplate) {
SaBasicUtil.saBasicTemplate = saBasicTemplate;
}
/**
* 注入 Sa-Token-SSO 单点登录模块 Bean
*

View File

@ -0,0 +1,40 @@
package cn.dev33.satoken.reactor.spring.oauth2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Template;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Util;
/**
* 注入 Sa-Token-OAuth2 所需要的Bean
*
* @author kong
*
*/
@ConditionalOnClass(SaOAuth2Manager.class)
public class SaOAuth2BeanInject {
/**
* 注入OAuth2配置Bean
*
* @param saOAuth2Config 配置对象
*/
@Autowired(required = false)
public void setSaOAuth2Config(SaOAuth2Config saOAuth2Config) {
SaOAuth2Manager.setConfig(saOAuth2Config);
}
/**
* 注入代码模板Bean
*
* @param saOAuth2Template 代码模板Bean
*/
@Autowired(required = false)
public void setSaOAuth2Interface(SaOAuth2Template saOAuth2Template) {
SaOAuth2Util.saOAuth2Template = saOAuth2Template;
}
}

View File

@ -0,0 +1,28 @@
package cn.dev33.satoken.reactor.spring.oauth2;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
/**
* 注册 Sa-Token-OAuth2 所需要的Bean
* @author kong
*
*/
@ConditionalOnClass(SaOAuth2Manager.class)
public class SaOAuth2BeanRegister {
/**
* 获取OAuth2配置Bean
* @return 配置对象
*/
@Bean
@ConfigurationProperties(prefix = "sa-token.oauth2")
public SaOAuth2Config getSaOAuth2Config() {
return new SaOAuth2Config();
}
}

View File

@ -0,0 +1,4 @@
/**
* Sa-Token-OAuth2 模块自动化配置只有引入了Sa-Token-OAuth2模块后此包下的代码才会开始工作
*/
package cn.dev33.satoken.reactor.spring.oauth2;

View File

@ -1,4 +1,6 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.dev33.satoken.reactor.spring.SaBeanRegister,\
cn.dev33.satoken.reactor.spring.SaBeanInject,\
cn.dev33.satoken.reactor.spring.SaHistoryVersionInject
cn.dev33.satoken.reactor.spring.SaHistoryVersionInject,\
cn.dev33.satoken.reactor.spring.oauth2.SaOAuth2BeanRegister,\
cn.dev33.satoken.reactor.spring.oauth2.SaOAuth2BeanInject

View File

@ -62,6 +62,15 @@ public class SaResponseForServlet implements SaResponse {
response.addCookie(cookie);
}
/**
* 设置响应状态码
*/
@Override
public SaResponse setStatus(int sc) {
response.setStatus(sc);
return this;
}
/**
* 在响应头里写入一个值
*/
@ -84,4 +93,5 @@ public class SaResponseForServlet implements SaResponse {
return null;
}
}

View File

@ -11,6 +11,8 @@ import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaCheckRole;
import cn.dev33.satoken.annotation.SaCheckSafe;
import cn.dev33.satoken.basic.SaBasicTemplate;
import cn.dev33.satoken.basic.SaBasicUtil;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.id.SaIdTemplate;
@ -75,10 +77,16 @@ public class XPluginImp implements Plugin {
SaIdUtil.saIdTemplate = bw.raw();
});
// Sa-Token Http Basic 认证模块 Bean
Aop.getAsyn(SaBasicTemplate.class, bw->{
SaBasicUtil.saBasicTemplate = bw.raw();
});
// Sa-Token-SSO 单点登录模块 Bean
Aop.getAsyn(SaSsoTemplate.class, bw->{
SaSsoUtil.saSsoTemplate = bw.raw();
});
}
}

View File

@ -36,6 +36,12 @@ public class SaResponseForSolon implements SaResponse {
ctx.cookieSet(name, value, domain, path, timeout);
}
@Override
public SaResponse setStatus(int sc) {
ctx.statusSet(sc);
return this;
}
@Override
public SaResponse setHeader(String name, String value) {
ctx.headerSet(name, value);

View File

@ -6,6 +6,8 @@ import org.springframework.util.PathMatcher;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.action.SaTokenAction;
import cn.dev33.satoken.basic.SaBasicTemplate;
import cn.dev33.satoken.basic.SaBasicUtil;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.context.SaTokenContext;
import cn.dev33.satoken.dao.SaTokenDao;
@ -105,6 +107,16 @@ public class SaBeanInject {
SaIdUtil.saIdTemplate = saIdTemplate;
}
/**
* 注入 Sa-Token Http Basic 认证模块
*
* @param saBasicTemplate saBasicTemplate对象
*/
@Autowired(required = false)
public void setSaSsoTemplate(SaBasicTemplate saBasicTemplate) {
SaBasicUtil.saBasicTemplate = saBasicTemplate;
}
/**
* 注入 Sa-Token-SSO 单点登录模块 Bean
*