fix bugs
This commit is contained in:
46
blade-core-secure/pom.xml
Normal file
46
blade-core-secure/pom.xml
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>BladeX-Tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>blade-core-secure</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<version>${project.parent.version}</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<!--Blade-->
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-starter-auth</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-starter-cache</artifactId>
|
||||
</dependency>
|
||||
<!--Jdbc-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>tomcat-jdbc</artifactId>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- Auto -->
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-core-auto</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 权限注解 用于检查权限 规定访问权限
|
||||
*
|
||||
* @example @PreAuth("#userVO.id<10")
|
||||
* @example @PreAuth("hasRole(#test, #test1)")
|
||||
* @example @PreAuth("hasPermission(#test) and @PreAuth.hasPermission(#test)")
|
||||
* @author Chill
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface PreAuth {
|
||||
|
||||
/**
|
||||
* Spring el表达式
|
||||
*/
|
||||
String value();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.aspect;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springblade.core.secure.annotation.PreAuth;
|
||||
import org.springblade.core.secure.auth.AuthFun;
|
||||
import org.springblade.core.secure.exception.SecureException;
|
||||
import org.springblade.core.tool.api.ResultCode;
|
||||
import org.springblade.core.tool.utils.ClassUtil;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.expression.BeanFactoryResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* AOP 鉴权
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Aspect
|
||||
public class AuthAspect implements ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* 表达式处理
|
||||
*/
|
||||
private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
|
||||
|
||||
/**
|
||||
* 切 方法 和 类上的 @PreAuth 注解
|
||||
*
|
||||
* @param point 切点
|
||||
* @return Object
|
||||
* @throws Throwable 没有权限的异常
|
||||
*/
|
||||
@Around(
|
||||
"@annotation(org.springblade.core.secure.annotation.PreAuth) || " +
|
||||
"@within(org.springblade.core.secure.annotation.PreAuth)"
|
||||
)
|
||||
public Object preAuth(ProceedingJoinPoint point) throws Throwable {
|
||||
if (handleAuth(point)) {
|
||||
return point.proceed();
|
||||
}
|
||||
throw new SecureException(ResultCode.UN_AUTHORIZED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理权限
|
||||
*
|
||||
* @param point 切点
|
||||
*/
|
||||
private boolean handleAuth(ProceedingJoinPoint point) {
|
||||
MethodSignature ms = (MethodSignature) point.getSignature();
|
||||
Method method = ms.getMethod();
|
||||
// 读取权限注解,优先方法上,没有则读取类
|
||||
PreAuth preAuth = ClassUtil.getAnnotation(method, PreAuth.class);
|
||||
// 判断表达式
|
||||
String condition = preAuth.value();
|
||||
if (StringUtil.isNotBlank(condition)) {
|
||||
Expression expression = EXPRESSION_PARSER.parseExpression(condition);
|
||||
// 方法参数值
|
||||
Object[] args = point.getArgs();
|
||||
StandardEvaluationContext context = getEvaluationContext(method, args);
|
||||
return expression.getValue(context, Boolean.class);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取方法上的参数
|
||||
*
|
||||
* @param method 方法
|
||||
* @param args 变量
|
||||
* @return {SimpleEvaluationContext}
|
||||
*/
|
||||
private StandardEvaluationContext getEvaluationContext(Method method, Object[] args) {
|
||||
// 初始化Sp el表达式上下文,并设置 AuthFun
|
||||
StandardEvaluationContext context = new StandardEvaluationContext(new AuthFun());
|
||||
// 设置表达式支持spring bean
|
||||
context.setBeanResolver(new BeanFactoryResolver(applicationContext));
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
// 读取方法参数
|
||||
MethodParameter methodParam = ClassUtil.getMethodParameter(method, i);
|
||||
// 设置方法 参数名和值 为sp el变量
|
||||
context.setVariable(methodParam.getParameterName(), args[i]);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.auth;
|
||||
|
||||
import org.springblade.core.jwt.JwtUtil;
|
||||
import org.springblade.core.launch.constant.TokenConstant;
|
||||
import org.springblade.core.secure.BladeUser;
|
||||
import org.springblade.core.secure.handler.IPermissionHandler;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.tool.constant.RoleConstant;
|
||||
import org.springblade.core.tool.utils.*;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 权限判断
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class AuthFun {
|
||||
|
||||
/**
|
||||
* 权限校验处理器
|
||||
*/
|
||||
private static IPermissionHandler permissionHandler;
|
||||
|
||||
private static IPermissionHandler getPermissionHandler() {
|
||||
if (permissionHandler == null) {
|
||||
permissionHandler = SpringUtil.getBean(IPermissionHandler.class);
|
||||
}
|
||||
return permissionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断角色是否具有接口权限
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
public boolean permissionAll() {
|
||||
return getPermissionHandler().permissionAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断角色是否具有接口权限
|
||||
*
|
||||
* @param permission 权限编号
|
||||
* @return {boolean}
|
||||
*/
|
||||
public boolean hasPermission(String permission) {
|
||||
return getPermissionHandler().hasPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 放行所有请求
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
public boolean permitAll() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 只有超管角色才可访问
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
public boolean denyAll() {
|
||||
return hasRole(RoleConstant.ADMIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否已授权
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
public boolean hasAuth() {
|
||||
return Func.isNotEmpty(AuthUtil.getUser());
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有时间授权
|
||||
*
|
||||
* @param start 开始时间
|
||||
* @param end 结束时间
|
||||
* @return {boolean}
|
||||
*/
|
||||
public boolean hasTimeAuth(Integer start, Integer end) {
|
||||
Integer hour = DateUtil.hour();
|
||||
return hour >= start && hour <= end;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否有该角色权限
|
||||
*
|
||||
* @param role 单角色
|
||||
* @return {boolean}
|
||||
*/
|
||||
public boolean hasRole(String role) {
|
||||
return hasAnyRole(role);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否具有所有角色权限
|
||||
*
|
||||
* @param role 角色集合
|
||||
* @return {boolean}
|
||||
*/
|
||||
public boolean hasAllRole(String... role) {
|
||||
for (String r : role) {
|
||||
if (!hasRole(r)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否有该角色权限
|
||||
*
|
||||
* @param role 角色集合
|
||||
* @return {boolean}
|
||||
*/
|
||||
public boolean hasAnyRole(String... role) {
|
||||
BladeUser user = AuthUtil.getUser();
|
||||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
String userRole = user.getRoleName();
|
||||
if (StringUtil.isBlank(userRole)) {
|
||||
return false;
|
||||
}
|
||||
String[] roles = Func.toStrArray(userRole);
|
||||
for (String r : role) {
|
||||
if (CollectionUtil.contains(roles, r)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断请求是否为加密token
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
public boolean hasCrypto() {
|
||||
HttpServletRequest request = WebUtil.getRequest();
|
||||
String auth = Objects.requireNonNull(request).getHeader(TokenConstant.HEADER);
|
||||
return JwtUtil.isCrypto(
|
||||
StringUtil.isNotBlank(auth) ? auth : request.getParameter(TokenConstant.HEADER)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断令牌是否符合严格模式
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
public boolean hasStrictToken() {
|
||||
BladeUser currentUser = AuthUtil.getUser();
|
||||
return AuthUtil.userIncomplete(currentUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否包含安全请求头
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
public boolean hasStrictHeader() {
|
||||
return !AuthUtil.secureHeaderIncomplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否有该请求头
|
||||
*
|
||||
* @param header 请求头
|
||||
* @return {boolean}
|
||||
*/
|
||||
public boolean hasHeader(String header) {
|
||||
HttpServletRequest request = WebUtil.getRequest();
|
||||
String value = Objects.requireNonNull(request).getHeader(header);
|
||||
return StringUtil.isNotBlank(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否有该请求头
|
||||
*
|
||||
* @param header 请求头
|
||||
* @param key 请求值
|
||||
* @return {boolean}
|
||||
*/
|
||||
public boolean hasHeader(String header, String key) {
|
||||
HttpServletRequest request = WebUtil.getRequest();
|
||||
String value = Objects.requireNonNull(request).getHeader(header);
|
||||
return StringUtil.equals(value, key);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.config;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.secure.handler.BladePermissionHandler;
|
||||
import org.springblade.core.secure.handler.IPermissionHandler;
|
||||
import org.springblade.core.secure.handler.ISecureHandler;
|
||||
import org.springblade.core.secure.handler.SecureHandlerHandler;
|
||||
import org.springblade.core.secure.registry.SecureRegistry;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
/**
|
||||
* secure注册默认配置
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Order
|
||||
@AutoConfiguration(before = SecureConfiguration.class)
|
||||
@AllArgsConstructor
|
||||
public class RegistryConfiguration {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(SecureRegistry.class)
|
||||
public SecureRegistry secureRegistry() {
|
||||
return new SecureRegistry();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ISecureHandler.class)
|
||||
public ISecureHandler secureHandler() {
|
||||
return new SecureHandlerHandler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(IPermissionHandler.class)
|
||||
public IPermissionHandler permissionHandler() {
|
||||
return new BladePermissionHandler(jdbcTemplate);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.config;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.launch.props.BladeProperties;
|
||||
import org.springblade.core.secure.aspect.AuthAspect;
|
||||
import org.springblade.core.secure.handler.ISecureHandler;
|
||||
import org.springblade.core.secure.props.AuthSecure;
|
||||
import org.springblade.core.secure.props.BasicSecure;
|
||||
import org.springblade.core.secure.props.BladeSecureProperties;
|
||||
import org.springblade.core.secure.props.SignSecure;
|
||||
import org.springblade.core.secure.registry.SecureRegistry;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 安全配置类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Order
|
||||
@AutoConfiguration
|
||||
@AllArgsConstructor
|
||||
@EnableConfigurationProperties({BladeSecureProperties.class})
|
||||
public class SecureConfiguration implements WebMvcConfigurer {
|
||||
|
||||
private final SecureRegistry secureRegistry;
|
||||
|
||||
private final BladeProperties bladeProperties;
|
||||
|
||||
private final BladeSecureProperties secureProperties;
|
||||
|
||||
private final ISecureHandler secureHandler;
|
||||
|
||||
@Override
|
||||
public void addInterceptors(@NonNull InterceptorRegistry registry) {
|
||||
// 设置请求授权
|
||||
if (secureRegistry.isAuthEnabled() || secureProperties.getAuthEnabled()) {
|
||||
List<AuthSecure> authSecures = this.secureRegistry.addAuthPatterns(secureProperties.getAuth()).getAuthSecures();
|
||||
if (!authSecures.isEmpty()) {
|
||||
registry.addInterceptor(secureHandler.authInterceptor(secureProperties, authSecures));
|
||||
// 设置路径放行
|
||||
secureRegistry.excludePathPatterns(authSecures.stream().map(AuthSecure::getPattern).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
// 设置基础认证授权
|
||||
if (secureRegistry.isBasicEnabled() || secureProperties.getBasicEnabled()) {
|
||||
List<BasicSecure> basicSecures = this.secureRegistry.addBasicPatterns(secureProperties.getBasic()).getBasicSecures();
|
||||
if (!basicSecures.isEmpty()) {
|
||||
registry.addInterceptor(secureHandler.basicInterceptor(basicSecures));
|
||||
// 设置路径放行
|
||||
secureRegistry.excludePathPatterns(basicSecures.stream().map(BasicSecure::getPattern).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
// 设置签名认证授权
|
||||
if (secureRegistry.isSignEnabled() || secureProperties.getSignEnabled()) {
|
||||
List<SignSecure> signSecures = this.secureRegistry.addSignPatterns(secureProperties.getSign()).getSignSecures();
|
||||
if (!signSecures.isEmpty()) {
|
||||
registry.addInterceptor(secureHandler.signInterceptor(signSecures));
|
||||
// 设置路径放行
|
||||
secureRegistry.excludePathPatterns(signSecures.stream().map(SignSecure::getPattern).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
// 设置客户端授权
|
||||
if (secureRegistry.isClientEnabled() || secureProperties.getClientEnabled()) {
|
||||
secureProperties.getClient().forEach(
|
||||
clientSecure -> registry.addInterceptor(secureHandler.clientInterceptor(clientSecure.getClientId()))
|
||||
.addPathPatterns(clientSecure.getPathPatterns())
|
||||
);
|
||||
}
|
||||
// 设置令牌严格模式
|
||||
if (!secureRegistry.isStrictToken()) {
|
||||
secureProperties.setStrictToken(false);
|
||||
}
|
||||
// 设置请求头严格模式
|
||||
if (!secureRegistry.isStrictHeader()) {
|
||||
secureProperties.setStrictHeader(false);
|
||||
}
|
||||
// 设置路径放行
|
||||
if (secureRegistry.isEnabled() || secureProperties.getEnabled()) {
|
||||
InterceptorRegistration interceptorRegistration = registry.addInterceptor(secureHandler.tokenInterceptor(secureProperties))
|
||||
.excludePathPatterns(secureRegistry.getExcludePatterns())
|
||||
.excludePathPatterns(secureRegistry.getDefaultExcludePatterns())
|
||||
.excludePathPatterns(secureProperties.getSkipUrl());
|
||||
// 宽松模式下获取放行路径且再新建一套自定义放行路径,用于处理cloud网关虚拟路径导致未匹配的问题
|
||||
// 严格模式下不予处理,应严格按照cloud和boot的路由进行匹配
|
||||
if (!secureProperties.getStrictToken()) {
|
||||
interceptorRegistration.excludePathPatterns(secureProperties.getSkipUrl().stream()
|
||||
.map(url -> StringUtil.removePrefix(url, StringPool.SLASH + bladeProperties.getName())).toList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthAspect authAspect() {
|
||||
return new AuthAspect();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.constant;
|
||||
|
||||
/**
|
||||
* PreAuth权限表达式
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface AuthConstant {
|
||||
|
||||
/**
|
||||
* 超管别名
|
||||
*/
|
||||
String ADMINISTRATOR = "administrator";
|
||||
|
||||
/**
|
||||
* 是有超管角色
|
||||
*/
|
||||
String HAS_ROLE_ADMINISTRATOR = "hasRole('" + ADMINISTRATOR + "')";
|
||||
|
||||
/**
|
||||
* 管理员别名
|
||||
*/
|
||||
String ADMIN = "admin";
|
||||
|
||||
/**
|
||||
* 是否有管理员角色
|
||||
*/
|
||||
String HAS_ROLE_ADMIN = "hasAnyRole('" + ADMINISTRATOR + "', '" + ADMIN + "')";
|
||||
|
||||
/**
|
||||
* 用户别名
|
||||
*/
|
||||
String USER = "user";
|
||||
|
||||
/**
|
||||
* 是否有用户角色
|
||||
*/
|
||||
String HAS_ROLE_USER = "hasRole('" + USER + "')";
|
||||
|
||||
/**
|
||||
* 测试别名
|
||||
*/
|
||||
String TEST = "test";
|
||||
|
||||
/**
|
||||
* 是否有测试角色
|
||||
*/
|
||||
String HAS_ROLE_TEST = "hasRole('" + TEST + "')";
|
||||
|
||||
/**
|
||||
* 放行所有请求
|
||||
*/
|
||||
String PERMIT_ALL = "permitAll()";
|
||||
|
||||
/**
|
||||
* 只有超管才能访问
|
||||
*/
|
||||
String DENY_ALL = "denyAll()";
|
||||
|
||||
/**
|
||||
* 对所有请求进行接口权限校验
|
||||
*/
|
||||
String PERMISSION_ALL = "permissionAll()";
|
||||
|
||||
/**
|
||||
* 是否对token加密传输
|
||||
*/
|
||||
String HAS_CRYPTO = "hasCrypto()";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.constant;
|
||||
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
|
||||
/**
|
||||
* 权限校验常量
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface PermissionConstant {
|
||||
|
||||
/**
|
||||
* 获取角色所有的权限编号
|
||||
*
|
||||
* @param size 数量
|
||||
* @return string
|
||||
*/
|
||||
static String permissionAllStatement(int size) {
|
||||
return "select scope_path as path from blade_scope_api where id in (select scope_id from blade_role_scope where scope_category = 2 and role_id in (" + buildHolder(size) + "))";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色指定的权限编号
|
||||
*
|
||||
* @param size 数量
|
||||
* @return string
|
||||
*/
|
||||
static String permissionStatement(int size) {
|
||||
return "select resource_code as code from blade_scope_api where resource_code = ? and id in (select scope_id from blade_role_scope where scope_category = 2 and role_id in (" + buildHolder(size) + "))";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Sql占位符
|
||||
*
|
||||
* @param size 数量
|
||||
* @return String
|
||||
*/
|
||||
static String buildHolder(int size) {
|
||||
StringBuilder builder = StringUtil.builder();
|
||||
for (int i = 0; i < size; i++) {
|
||||
builder.append("?,");
|
||||
}
|
||||
return StringUtil.removeSuffix(builder.toString(), ",");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.constant;
|
||||
|
||||
/**
|
||||
* 授权校验常量
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface SecureConstant {
|
||||
|
||||
/**
|
||||
* 认证请求头
|
||||
*/
|
||||
String BASIC_HEADER_KEY = "Authorization";
|
||||
|
||||
/**
|
||||
* 认证请求头前缀
|
||||
*/
|
||||
String BASIC_HEADER_PREFIX = "Basic ";
|
||||
|
||||
/**
|
||||
* 认证请求头前缀
|
||||
*/
|
||||
String BASIC_HEADER_PREFIX_EXT = "Basic%20";
|
||||
|
||||
/**
|
||||
* 认证请求头
|
||||
*/
|
||||
String BASIC_REALM_HEADER_KEY = "WWW-Authenticate";
|
||||
|
||||
/**
|
||||
* 认证请求值
|
||||
*/
|
||||
String BASIC_REALM_HEADER_VALUE = "basic realm=\"no auth\"";
|
||||
|
||||
/**
|
||||
* 授权认证失败
|
||||
*/
|
||||
String AUTHORIZATION_FAILED = "授权认证失败";
|
||||
|
||||
/**
|
||||
* 签名认证失败
|
||||
*/
|
||||
String SIGN_FAILED = "签名认证失败";
|
||||
|
||||
/**
|
||||
* 用户信息不完整
|
||||
*/
|
||||
String USER_INCOMPLETE = "用户信息不完整,签名认证失败";
|
||||
/**
|
||||
* 请求头信息不完整
|
||||
*/
|
||||
String SECURE_HEADER_INCOMPLETE = "请求头信息不完整,签名认证失败";
|
||||
/**
|
||||
* 客户端令牌解析失败
|
||||
*/
|
||||
String CLIENT_TOKEN_PARSE_FAILED = "客户端令牌解析失败";
|
||||
/**
|
||||
* 客户端令牌不合法
|
||||
*/
|
||||
String INVALID_CLIENT_TOKEN = "客户端令牌不合法";
|
||||
/**
|
||||
* Authorization未找到
|
||||
*/
|
||||
String AUTHORIZATION_NOT_FOUND = "请求头中未找到 [Authorization] 信息";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.handler;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.cache.utils.CacheUtil;
|
||||
import org.springblade.core.secure.BladeUser;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springblade.core.tool.utils.WebUtil;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.springblade.core.cache.constant.CacheConstant.SYS_CACHE;
|
||||
import static org.springblade.core.secure.constant.PermissionConstant.permissionAllStatement;
|
||||
import static org.springblade.core.secure.constant.PermissionConstant.permissionStatement;
|
||||
|
||||
/**
|
||||
* 默认授权校验类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class BladePermissionHandler implements IPermissionHandler {
|
||||
|
||||
private static final String SCOPE_CACHE_CODE = "apiScope:code:";
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Override
|
||||
public boolean permissionAll() {
|
||||
HttpServletRequest request = WebUtil.getRequest();
|
||||
BladeUser user = AuthUtil.getUser();
|
||||
if (request == null || user == null) {
|
||||
return false;
|
||||
}
|
||||
String uri = request.getRequestURI();
|
||||
List<String> paths = permissionPath(user.getRoleId());
|
||||
if (paths.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
return paths.stream().anyMatch(uri::contains);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
HttpServletRequest request = WebUtil.getRequest();
|
||||
BladeUser user = AuthUtil.getUser();
|
||||
if (request == null || user == null) {
|
||||
return false;
|
||||
}
|
||||
List<String> codes = permissionCode(permission, user.getRoleId());
|
||||
return codes.size() != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取接口权限地址
|
||||
*
|
||||
* @param roleId 角色id
|
||||
* @return permissions
|
||||
*/
|
||||
private List<String> permissionPath(String roleId) {
|
||||
List<String> permissions = CacheUtil.get(SYS_CACHE, SCOPE_CACHE_CODE, roleId, List.class, Boolean.FALSE);
|
||||
if (permissions == null) {
|
||||
List<Long> roleIds = Func.toLongList(roleId);
|
||||
permissions = jdbcTemplate.queryForList(permissionAllStatement(roleIds.size()), roleIds.toArray(), String.class);
|
||||
CacheUtil.put(SYS_CACHE, SCOPE_CACHE_CODE, roleId, permissions, Boolean.FALSE);
|
||||
}
|
||||
return permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取接口权限信息
|
||||
*
|
||||
* @param permission 权限编号
|
||||
* @param roleId 角色id
|
||||
* @return permissions
|
||||
*/
|
||||
private List<String> permissionCode(String permission, String roleId) {
|
||||
List<String> permissions = CacheUtil.get(SYS_CACHE, SCOPE_CACHE_CODE, permission + StringPool.COLON + roleId, List.class, Boolean.FALSE);
|
||||
if (permissions == null) {
|
||||
List<Object> args = new ArrayList<>(Collections.singletonList(permission));
|
||||
List<Long> roleIds = Func.toLongList(roleId);
|
||||
args.addAll(roleIds);
|
||||
permissions = jdbcTemplate.queryForList(permissionStatement(roleIds.size()), args.toArray(), String.class);
|
||||
CacheUtil.put(SYS_CACHE, SCOPE_CACHE_CODE, permission + StringPool.COLON + roleId, permissions, Boolean.FALSE);
|
||||
}
|
||||
return permissions;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.handler;
|
||||
|
||||
/**
|
||||
* 权限校验通用接口
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface IPermissionHandler {
|
||||
|
||||
/**
|
||||
* 判断角色是否具有接口权限
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
boolean permissionAll();
|
||||
|
||||
/**
|
||||
* 判断角色是否具有接口权限
|
||||
*
|
||||
* @param permission 权限编号
|
||||
* @return {boolean}
|
||||
*/
|
||||
boolean hasPermission(String permission);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.handler;
|
||||
|
||||
import org.springblade.core.secure.props.AuthSecure;
|
||||
import org.springblade.core.secure.props.BasicSecure;
|
||||
import org.springblade.core.secure.props.BladeSecureProperties;
|
||||
import org.springblade.core.secure.props.SignSecure;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* secure 拦截器集合
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface ISecureHandler {
|
||||
|
||||
/**
|
||||
* token拦截器
|
||||
*
|
||||
* @param secureProperties 授权配置
|
||||
* @return tokenInterceptor
|
||||
*/
|
||||
HandlerInterceptor tokenInterceptor(BladeSecureProperties secureProperties);
|
||||
|
||||
/**
|
||||
* auth拦截器
|
||||
*
|
||||
* @param authSecures 授权集合
|
||||
* @return HandlerInterceptor
|
||||
*/
|
||||
HandlerInterceptor authInterceptor(BladeSecureProperties secureProperties, List<AuthSecure> authSecures);
|
||||
|
||||
/**
|
||||
* basic拦截器
|
||||
*
|
||||
* @param basicSecures 基础认证集合
|
||||
* @return HandlerInterceptor
|
||||
*/
|
||||
HandlerInterceptor basicInterceptor(List<BasicSecure> basicSecures);
|
||||
|
||||
/**
|
||||
* sign拦截器
|
||||
*
|
||||
* @param signSecures 签名认证集合
|
||||
* @return HandlerInterceptor
|
||||
*/
|
||||
HandlerInterceptor signInterceptor(List<SignSecure> signSecures);
|
||||
|
||||
/**
|
||||
* client拦截器
|
||||
*
|
||||
* @param clientId 客户端id
|
||||
* @return clientInterceptor
|
||||
*/
|
||||
HandlerInterceptor clientInterceptor(String clientId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.handler;
|
||||
|
||||
import org.springblade.core.secure.interceptor.*;
|
||||
import org.springblade.core.secure.props.AuthSecure;
|
||||
import org.springblade.core.secure.props.BasicSecure;
|
||||
import org.springblade.core.secure.props.BladeSecureProperties;
|
||||
import org.springblade.core.secure.props.SignSecure;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Secure处理器
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class SecureHandlerHandler implements ISecureHandler {
|
||||
|
||||
@Override
|
||||
public HandlerInterceptor tokenInterceptor(BladeSecureProperties secureProperties) {
|
||||
return new TokenInterceptor(secureProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerInterceptor authInterceptor(BladeSecureProperties secureProperties, List<AuthSecure> authSecures) {
|
||||
return new AuthInterceptor(authSecures);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerInterceptor basicInterceptor(List<BasicSecure> basicSecures) {
|
||||
return new BasicInterceptor(basicSecures);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerInterceptor signInterceptor(List<SignSecure> signSecures) {
|
||||
return new SignInterceptor(signSecures);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerInterceptor clientInterceptor(String clientId) {
|
||||
return new ClientInterceptor(clientId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.interceptor;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.secure.auth.AuthFun;
|
||||
import org.springblade.core.secure.props.AuthSecure;
|
||||
import org.springblade.core.secure.provider.HttpMethod;
|
||||
import org.springblade.core.secure.provider.ResponseProvider;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.springblade.core.secure.constant.SecureConstant.AUTHORIZATION_FAILED;
|
||||
|
||||
/**
|
||||
* 自定义授权拦截器校验
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class AuthInterceptor implements HandlerInterceptor {
|
||||
|
||||
/**
|
||||
* 表达式处理
|
||||
*/
|
||||
private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
|
||||
private static final EvaluationContext EVALUATION_CONTEXT = new StandardEvaluationContext(new AuthFun());
|
||||
private static final AntPathMatcher ANT_PATH_MATCHER = new AntPathMatcher();
|
||||
|
||||
/**
|
||||
* 授权集合
|
||||
*/
|
||||
private final List<AuthSecure> authSecures;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) {
|
||||
boolean check = authSecures.stream().filter(authSecure -> checkAuth(request, authSecure)).findFirst().map(
|
||||
authSecure -> checkExpression(authSecure.getExpression())
|
||||
).orElse(Boolean.TRUE);
|
||||
if (!check) {
|
||||
ResponseProvider.logAuthFailure(request, response, AUTHORIZATION_FAILED);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测授权
|
||||
*/
|
||||
private boolean checkAuth(HttpServletRequest request, AuthSecure authSecure) {
|
||||
return checkMethod(request, authSecure.getMethod()) && checkPath(request, authSecure.getPattern());
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测请求方法
|
||||
*/
|
||||
private boolean checkMethod(HttpServletRequest request, HttpMethod method) {
|
||||
return method == HttpMethod.ALL || (
|
||||
method != null && method == HttpMethod.of(request.getMethod())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测路径匹配
|
||||
*/
|
||||
private boolean checkPath(HttpServletRequest request, String pattern) {
|
||||
String servletPath = request.getServletPath();
|
||||
String pathInfo = request.getPathInfo();
|
||||
if (pathInfo != null && !pathInfo.isEmpty()) {
|
||||
servletPath = servletPath + pathInfo;
|
||||
}
|
||||
return ANT_PATH_MATCHER.match(pattern, servletPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测表达式
|
||||
*/
|
||||
private boolean checkExpression(String expression) {
|
||||
Boolean result = EXPRESSION_PARSER.parseExpression(expression).getValue(EVALUATION_CONTEXT, Boolean.class);
|
||||
return result != null ? result : false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.interceptor;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.secure.props.BasicSecure;
|
||||
import org.springblade.core.secure.provider.HttpMethod;
|
||||
import org.springblade.core.secure.provider.ResponseProvider;
|
||||
import org.springblade.core.secure.utils.SecureUtil;
|
||||
import org.springblade.core.tool.jackson.JsonUtil;
|
||||
import org.springblade.core.tool.utils.WebUtil;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.springblade.core.secure.constant.SecureConstant.BASIC_REALM_HEADER_KEY;
|
||||
import static org.springblade.core.secure.constant.SecureConstant.BASIC_REALM_HEADER_VALUE;
|
||||
|
||||
/**
|
||||
* 基础认证拦截器校验
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class BasicInterceptor implements HandlerInterceptor {
|
||||
|
||||
/**
|
||||
* 表达式匹配
|
||||
*/
|
||||
private static final AntPathMatcher ANT_PATH_MATCHER = new AntPathMatcher();
|
||||
|
||||
/**
|
||||
* 授权集合
|
||||
*/
|
||||
private final List<BasicSecure> basicSecures;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) {
|
||||
boolean check = basicSecures.stream().filter(basicSecure -> checkAuth(request, basicSecure)).findFirst().map(
|
||||
authSecure -> checkBasic(authSecure.getUsername(), authSecure.getPassword())
|
||||
).orElse(Boolean.TRUE);
|
||||
if (!check) {
|
||||
log.warn("授权认证失败,请求接口:{},请求IP:{},请求参数:{}", request.getRequestURI(), WebUtil.getIP(request), JsonUtil.toJson(request.getParameterMap()));
|
||||
response.setHeader(BASIC_REALM_HEADER_KEY, BASIC_REALM_HEADER_VALUE);
|
||||
ResponseProvider.write(response);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测授权
|
||||
*/
|
||||
private boolean checkAuth(HttpServletRequest request, BasicSecure basicSecure) {
|
||||
return checkMethod(request, basicSecure.getMethod()) && checkPath(request, basicSecure.getPattern());
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测请求方法
|
||||
*/
|
||||
private boolean checkMethod(HttpServletRequest request, HttpMethod method) {
|
||||
return method == HttpMethod.ALL || (
|
||||
method != null && method == HttpMethod.of(request.getMethod())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测路径匹配
|
||||
*/
|
||||
private boolean checkPath(HttpServletRequest request, String pattern) {
|
||||
String servletPath = request.getServletPath();
|
||||
String pathInfo = request.getPathInfo();
|
||||
if (pathInfo != null && !pathInfo.isEmpty()) {
|
||||
servletPath = servletPath + pathInfo;
|
||||
}
|
||||
return ANT_PATH_MATCHER.match(pattern, servletPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测表达式
|
||||
*/
|
||||
private boolean checkBasic(String username, String password) {
|
||||
try {
|
||||
String[] tokens = SecureUtil.extractAndDecodeAuthorization();
|
||||
return username.equals(tokens[0]) && password.equals(tokens[1]);
|
||||
} catch (Exception e) {
|
||||
log.warn("授权认证失败,错误信息:{}", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.interceptor;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.secure.BladeUser;
|
||||
import org.springblade.core.secure.provider.ResponseProvider;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.secure.utils.SecureUtil;
|
||||
import org.springblade.core.tool.jackson.JsonUtil;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.core.tool.utils.WebUtil;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
/**
|
||||
* 客户端校验拦截器
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class ClientInterceptor implements HandlerInterceptor {
|
||||
|
||||
private final String clientId;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) {
|
||||
BladeUser user = AuthUtil.getUser();
|
||||
boolean check = (
|
||||
user != null &&
|
||||
StringUtil.equals(clientId, SecureUtil.getClientId()) &&
|
||||
StringUtil.equals(clientId, user.getClientId())
|
||||
);
|
||||
if (!check) {
|
||||
log.warn("客户端认证失败,请求接口:{},请求IP:{},请求参数:{}", request.getRequestURI(), WebUtil.getIP(request), JsonUtil.toJson(request.getParameterMap()));
|
||||
ResponseProvider.write(response);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.interceptor;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.secure.props.SignSecure;
|
||||
import org.springblade.core.secure.provider.HttpMethod;
|
||||
import org.springblade.core.secure.provider.ResponseProvider;
|
||||
import org.springblade.core.tool.jackson.JsonUtil;
|
||||
import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springblade.core.tool.utils.DigestUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.core.tool.utils.WebUtil;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 签名认证拦截器校验
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class SignInterceptor implements HandlerInterceptor {
|
||||
|
||||
/**
|
||||
* 表达式匹配
|
||||
*/
|
||||
private static final AntPathMatcher ANT_PATH_MATCHER = new AntPathMatcher();
|
||||
|
||||
/**
|
||||
* 授权集合
|
||||
*/
|
||||
private final List<SignSecure> signSecures;
|
||||
|
||||
/**
|
||||
* 请求时间
|
||||
*/
|
||||
private final static String TIMESTAMP = "timestamp";
|
||||
|
||||
/**
|
||||
* 随机数
|
||||
*/
|
||||
private final static String NONCE = "nonce";
|
||||
|
||||
/**
|
||||
* 时间随机数组合加密串
|
||||
*/
|
||||
private final static String SIGNATURE = "signature";
|
||||
|
||||
/**
|
||||
* sha1加密方式
|
||||
*/
|
||||
private final static String SHA1 = "sha1";
|
||||
|
||||
/**
|
||||
* md5加密方式
|
||||
*/
|
||||
private final static String MD5 = "md5";
|
||||
|
||||
/**
|
||||
* 时间差最小值
|
||||
*/
|
||||
private final static Integer SECOND_MIN = 0;
|
||||
|
||||
/**
|
||||
* 时间差最大值
|
||||
*/
|
||||
private final static Integer SECOND_MAX = 10;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) {
|
||||
boolean check = signSecures.stream().filter(signSecure -> checkAuth(request, signSecure)).findFirst().map(
|
||||
authSecure -> checkSign(authSecure.getCrypto())
|
||||
).orElse(Boolean.TRUE);
|
||||
if (!check) {
|
||||
log.warn("授权认证失败,请求接口:{},请求IP:{},请求参数:{}", request.getRequestURI(), WebUtil.getIP(request), JsonUtil.toJson(request.getParameterMap()));
|
||||
ResponseProvider.write(response);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测授权
|
||||
*/
|
||||
private boolean checkAuth(HttpServletRequest request, SignSecure signSecure) {
|
||||
return checkMethod(request, signSecure.getMethod()) && checkPath(request, signSecure.getPattern());
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测请求方法
|
||||
*/
|
||||
private boolean checkMethod(HttpServletRequest request, HttpMethod method) {
|
||||
return method == HttpMethod.ALL || (
|
||||
method != null && method == HttpMethod.of(request.getMethod())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测路径匹配
|
||||
*/
|
||||
private boolean checkPath(HttpServletRequest request, String pattern) {
|
||||
String servletPath = request.getServletPath();
|
||||
String pathInfo = request.getPathInfo();
|
||||
if (pathInfo != null && pathInfo.length() > 0) {
|
||||
servletPath = servletPath + pathInfo;
|
||||
}
|
||||
return ANT_PATH_MATCHER.match(pattern, servletPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测表达式
|
||||
*/
|
||||
private boolean checkSign(String crypto) {
|
||||
try {
|
||||
HttpServletRequest request = WebUtil.getRequest();
|
||||
if (request == null) {
|
||||
return false;
|
||||
}
|
||||
// 获取头部动态签名信息
|
||||
String timestamp = request.getHeader(TIMESTAMP);
|
||||
// 判断是否在合法时间段
|
||||
long seconds = Duration.between(new Date(Func.toLong(timestamp)).toInstant(), DateUtil.now().toInstant()).getSeconds();
|
||||
if (seconds < SECOND_MIN || seconds > SECOND_MAX) {
|
||||
log.warn("授权认证失败,错误信息:{}", "请求时间戳非法");
|
||||
return false;
|
||||
}
|
||||
String nonce = request.getHeader(NONCE);
|
||||
String signature = request.getHeader(SIGNATURE);
|
||||
// 加密签名比对,可自行拓展加密规则
|
||||
String sign;
|
||||
if (crypto.equals(MD5)) {
|
||||
sign = DigestUtil.md5Hex(timestamp + nonce);
|
||||
} else if (crypto.equals(SHA1)) {
|
||||
sign = DigestUtil.sha1Hex(timestamp + nonce);
|
||||
} else {
|
||||
sign = DigestUtil.sha1Hex(timestamp + nonce);
|
||||
}
|
||||
return sign.equalsIgnoreCase(signature);
|
||||
} catch (Exception e) {
|
||||
log.warn("授权认证失败,错误信息:{}", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.interceptor;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.secure.BladeUser;
|
||||
import org.springblade.core.secure.props.BladeSecureProperties;
|
||||
import org.springblade.core.secure.provider.ResponseProvider;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.springblade.core.secure.constant.SecureConstant.*;
|
||||
|
||||
|
||||
/**
|
||||
* 签名认证拦截器
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class TokenInterceptor implements HandlerInterceptor {
|
||||
|
||||
private final BladeSecureProperties secureProperties;
|
||||
private final static List<String> DEFAULT_STRICT_SKIP_URL = new ArrayList<>();
|
||||
|
||||
static {
|
||||
DEFAULT_STRICT_SKIP_URL.add("/menu/routes");
|
||||
DEFAULT_STRICT_SKIP_URL.add("/menu/buttons");
|
||||
DEFAULT_STRICT_SKIP_URL.add("/menu/top-menu");
|
||||
DEFAULT_STRICT_SKIP_URL.add("/blade-system/menu/routes");
|
||||
DEFAULT_STRICT_SKIP_URL.add("/blade-system/menu/buttons");
|
||||
DEFAULT_STRICT_SKIP_URL.add("/blade-system/menu/top-menu");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) {
|
||||
BladeUser currentUser = AuthUtil.getUser();
|
||||
if (currentUser == null) {
|
||||
ResponseProvider.logAuthFailure(request, response, SIGN_FAILED);
|
||||
return false;
|
||||
}
|
||||
if (checkStrictToken(request, currentUser)) {
|
||||
ResponseProvider.logAuthFailure(request, response, USER_INCOMPLETE);
|
||||
return false;
|
||||
}
|
||||
if (checkStrictHeader()) {
|
||||
ResponseProvider.logAuthFailure(request, response, SECURE_HEADER_INCOMPLETE);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean checkStrictToken(HttpServletRequest request, BladeUser currentUser) {
|
||||
String requestUrl = request.getRequestURI(); // 获取当前请求的URL
|
||||
boolean skip = DEFAULT_STRICT_SKIP_URL.stream()
|
||||
.anyMatch(requestUrl::equals); // 判断当前请求的URL是否在跳过列表中
|
||||
|
||||
// 如果请求的URL需要跳过检查或者不需要严格检查Token,则返回false
|
||||
return !skip && secureProperties.getStrictToken() && AuthUtil.userIncomplete(currentUser);
|
||||
}
|
||||
|
||||
private boolean checkStrictHeader() {
|
||||
return secureProperties.getStrictHeader() && AuthUtil.secureHeaderIncomplete();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.props;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springblade.core.secure.provider.HttpMethod;
|
||||
|
||||
/**
|
||||
* 自定义授权规则
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AuthSecure {
|
||||
/**
|
||||
* 请求方法
|
||||
*/
|
||||
private HttpMethod method;
|
||||
/**
|
||||
* 请求路径
|
||||
*/
|
||||
private String pattern;
|
||||
/**
|
||||
* 规则表达式
|
||||
*/
|
||||
private String expression;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.props;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springblade.core.secure.provider.HttpMethod;
|
||||
|
||||
/**
|
||||
* 基础授权规则
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BasicSecure {
|
||||
|
||||
/**
|
||||
* 请求方法
|
||||
*/
|
||||
private HttpMethod method;
|
||||
/**
|
||||
* 请求路径
|
||||
*/
|
||||
private String pattern;
|
||||
/**
|
||||
* 客户端id
|
||||
*/
|
||||
private String username;
|
||||
/**
|
||||
* 客户端密钥
|
||||
*/
|
||||
private String password;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.props;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 客户端校验配置
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties("blade.secure")
|
||||
public class BladeSecureProperties {
|
||||
|
||||
/**
|
||||
* 开启鉴权规则
|
||||
*/
|
||||
private Boolean enabled = false;
|
||||
|
||||
/**
|
||||
* 开启令牌严格模式
|
||||
*/
|
||||
private Boolean strictToken = true;
|
||||
|
||||
/**
|
||||
* 开启请求头严格模式
|
||||
*/
|
||||
private Boolean strictHeader = true;
|
||||
|
||||
/**
|
||||
* 鉴权放行请求
|
||||
*/
|
||||
private final List<String> skipUrl = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 开启授权规则
|
||||
*/
|
||||
private Boolean authEnabled = true;
|
||||
|
||||
/**
|
||||
* 授权配置
|
||||
*/
|
||||
private final List<AuthSecure> auth = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 开启基础认证规则
|
||||
*/
|
||||
private Boolean basicEnabled = true;
|
||||
|
||||
/**
|
||||
* 基础认证配置
|
||||
*/
|
||||
private final List<BasicSecure> basic = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 开启签名认证规则
|
||||
*/
|
||||
private Boolean signEnabled = true;
|
||||
|
||||
/**
|
||||
* 签名认证配置
|
||||
*/
|
||||
private final List<SignSecure> sign = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 开启客户端规则
|
||||
*/
|
||||
private Boolean clientEnabled = true;
|
||||
|
||||
/**
|
||||
* 客户端配置
|
||||
*/
|
||||
private final List<ClientSecure> client = new ArrayList<>();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.props;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 客户端令牌认证信息
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
public class ClientSecure {
|
||||
|
||||
/**
|
||||
* 客户端ID
|
||||
*/
|
||||
private String clientId;
|
||||
/**
|
||||
* 路径匹配
|
||||
*/
|
||||
private final List<String> pathPatterns = new ArrayList<>();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.props;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springblade.core.secure.provider.HttpMethod;
|
||||
|
||||
/**
|
||||
* 签名授权规则
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SignSecure {
|
||||
|
||||
/**
|
||||
* 请求方法
|
||||
*/
|
||||
private HttpMethod method;
|
||||
/**
|
||||
* 请求路径
|
||||
*/
|
||||
private String pattern;
|
||||
/**
|
||||
* 加密方式
|
||||
*/
|
||||
private String crypto;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.provider;
|
||||
|
||||
/**
|
||||
* HttpMethod枚举类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public enum HttpMethod {
|
||||
|
||||
/**
|
||||
* 请求方法集合
|
||||
*/
|
||||
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE, ALL;
|
||||
|
||||
/**
|
||||
* 匹配枚举
|
||||
*/
|
||||
public static HttpMethod of(String method) {
|
||||
try {
|
||||
return valueOf(method);
|
||||
} catch (Exception exception) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.provider;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.api.ResultCode;
|
||||
import org.springblade.core.tool.constant.BladeConstant;
|
||||
import org.springblade.core.tool.jackson.JsonUtil;
|
||||
import org.springblade.core.tool.utils.WebUtil;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* ResponseProvider
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
public class ResponseProvider {
|
||||
|
||||
public static void logAuthFailure(HttpServletRequest request, HttpServletResponse response, String reason) {
|
||||
try {
|
||||
Map<String, String[]> parameterMap = request.getParameterMap();
|
||||
String paramsJson = JsonUtil.toJson(parameterMap);
|
||||
log.warn("{},请求接口:{},请求IP:{},请求参数:{}", reason, request.getRequestURI(), WebUtil.getIP(request), paramsJson);
|
||||
} catch (Exception e) {
|
||||
log.error("日志记录失败", e);
|
||||
}
|
||||
ResponseProvider.write(response);
|
||||
}
|
||||
|
||||
public static void write(HttpServletResponse response) {
|
||||
R result = R.fail(ResultCode.UN_AUTHORIZED);
|
||||
response.setCharacterEncoding(BladeConstant.UTF_8);
|
||||
response.addHeader(BladeConstant.CONTENT_TYPE_NAME, MediaType.APPLICATION_JSON_VALUE);
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
try {
|
||||
response.getWriter().write(Objects.requireNonNull(JsonUtil.toJson(result)));
|
||||
} catch (IOException ex) {
|
||||
log.error(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.registry;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import org.springblade.core.secure.props.AuthSecure;
|
||||
import org.springblade.core.secure.props.BasicSecure;
|
||||
import org.springblade.core.secure.props.SignSecure;
|
||||
import org.springblade.core.secure.provider.HttpMethod;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 安全框架统一配置
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
public class SecureRegistry {
|
||||
|
||||
/**
|
||||
* 是否开启鉴权
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* 开启令牌严格模式
|
||||
*/
|
||||
private boolean strictToken = true;
|
||||
|
||||
/**
|
||||
* 开启请求头严格模式
|
||||
*/
|
||||
private boolean strictHeader = true;
|
||||
|
||||
/**
|
||||
* 是否开启授权
|
||||
*/
|
||||
private boolean authEnabled = true;
|
||||
|
||||
/**
|
||||
* 是否开启基础认证
|
||||
*/
|
||||
private boolean basicEnabled = true;
|
||||
|
||||
/**
|
||||
* 是否开启签名认证
|
||||
*/
|
||||
private boolean signEnabled = true;
|
||||
|
||||
/**
|
||||
* 是否开启客户端认证
|
||||
*/
|
||||
private boolean clientEnabled = true;
|
||||
|
||||
/**
|
||||
* 默认放行规则
|
||||
*/
|
||||
private final List<String> defaultExcludePatterns = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 自定义放行规则
|
||||
*/
|
||||
private final List<String> excludePatterns = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 自定义授权集合
|
||||
*/
|
||||
@Getter
|
||||
private final List<AuthSecure> authSecures = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 基础认证集合
|
||||
*/
|
||||
@Getter
|
||||
private final List<BasicSecure> basicSecures = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 签名认证集合
|
||||
*/
|
||||
@Getter
|
||||
private final List<SignSecure> signSecures = new ArrayList<>();
|
||||
|
||||
public SecureRegistry() {
|
||||
this.defaultExcludePatterns.add("/actuator/health/**");
|
||||
this.defaultExcludePatterns.add("/v3/api-docs/**");
|
||||
this.defaultExcludePatterns.add("/swagger-ui/**");
|
||||
this.defaultExcludePatterns.add("/oauth/**");
|
||||
this.defaultExcludePatterns.add("/feign/client/**");
|
||||
this.defaultExcludePatterns.add("/process/resource-view");
|
||||
this.defaultExcludePatterns.add("/process/diagram-view");
|
||||
this.defaultExcludePatterns.add("/manager/check-upload");
|
||||
this.defaultExcludePatterns.add("/tenant/info");
|
||||
this.defaultExcludePatterns.add("/static/**");
|
||||
this.defaultExcludePatterns.add("/assets/**");
|
||||
this.defaultExcludePatterns.add("/error");
|
||||
this.defaultExcludePatterns.add("/favicon.ico");
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置单个放行api
|
||||
*/
|
||||
public SecureRegistry excludePathPattern(String pattern) {
|
||||
this.excludePatterns.add(pattern);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置放行api集合
|
||||
*/
|
||||
public SecureRegistry excludePathPatterns(String... patterns) {
|
||||
this.excludePatterns.addAll(Arrays.asList(patterns));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置放行api集合
|
||||
*/
|
||||
public void excludePathPatterns(List<String> patterns) {
|
||||
this.excludePatterns.addAll(patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置单个自定义授权
|
||||
*/
|
||||
public SecureRegistry addAuthPattern(HttpMethod method, String pattern, String expression) {
|
||||
this.authSecures.add(new AuthSecure(method, pattern, expression));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置自定义授权集合
|
||||
*/
|
||||
public SecureRegistry addAuthPatterns(List<AuthSecure> authSecures) {
|
||||
this.authSecures.addAll(authSecures);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置基础认证
|
||||
*/
|
||||
public SecureRegistry addBasicPattern(HttpMethod method, String pattern, String username, String password) {
|
||||
this.basicSecures.add(new BasicSecure(method, pattern, username, password));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置基础认证集合
|
||||
*/
|
||||
public SecureRegistry addBasicPatterns(List<BasicSecure> basicSecures) {
|
||||
this.basicSecures.addAll(basicSecures);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置签名认证
|
||||
*/
|
||||
public SecureRegistry addSignPattern(HttpMethod method, String pattern, String crypto) {
|
||||
this.signSecures.add(new SignSecure(method, pattern, crypto));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置签名认证集合
|
||||
*/
|
||||
public SecureRegistry addSignPatterns(List<SignSecure> signSecures) {
|
||||
this.signSecures.addAll(signSecures);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否开启令牌严格模式
|
||||
*/
|
||||
public SecureRegistry strictToken(boolean strictToken) {
|
||||
this.strictToken = strictToken;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否开启请求头严格模式
|
||||
*/
|
||||
public SecureRegistry strictHeader(boolean strictHeader) {
|
||||
this.strictHeader = strictHeader;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.secure.utils;
|
||||
|
||||
import io.jsonwebtoken.JwtBuilder;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springblade.core.jwt.JwtUtil;
|
||||
import org.springblade.core.secure.TokenInfo;
|
||||
import org.springblade.core.secure.constant.SecureConstant;
|
||||
import org.springblade.core.tool.support.Kv;
|
||||
import org.springblade.core.tool.utils.Charsets;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springblade.core.tool.utils.WebUtil;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.springblade.core.secure.constant.SecureConstant.*;
|
||||
|
||||
/**
|
||||
* Secure工具类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class SecureUtil extends AuthUtil {
|
||||
|
||||
public static final String TYP = "typ";
|
||||
public static final String JWT = "JWT";
|
||||
public static final String AUDIENCE = "bladex";
|
||||
public static final String ISSUER = "bladex.cn";
|
||||
|
||||
/**
|
||||
* 创建令牌
|
||||
*
|
||||
* @param kv 构建参数
|
||||
* @return TokenInfo
|
||||
*/
|
||||
public static TokenInfo createToken(Kv kv) {
|
||||
return createToken(kv, null, AUDIENCE, ISSUER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建令牌
|
||||
*
|
||||
* @param kv 构建参数
|
||||
* @param expire 过期秒数
|
||||
* @return TokenInfo
|
||||
*/
|
||||
public static TokenInfo createToken(Kv kv, Integer expire) {
|
||||
return createToken(kv, expire, AUDIENCE, ISSUER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建令牌
|
||||
*
|
||||
* @param kv 构建参数
|
||||
* @param expire 过期秒数
|
||||
* @param audience audience
|
||||
* @param issuer issuer
|
||||
* @return TokenInfo
|
||||
*/
|
||||
public static TokenInfo createToken(Kv kv, Integer expire, String audience, String issuer) {
|
||||
// 添加Token过期时间
|
||||
Instant now = Instant.now();
|
||||
int expireSeconds = Optional.ofNullable(expire)
|
||||
.orElseGet(SecureUtil::getExpire); // 获取默认过期时间
|
||||
Instant exp = now.plus(expireSeconds, ChronoUnit.SECONDS);
|
||||
|
||||
// 生成签名密钥
|
||||
SecretKey signingKey = Keys.hmacShaKeyFor(Base64.getDecoder().decode(JwtUtil.getBase64Security()));
|
||||
|
||||
// 添加构成JWT的类
|
||||
JwtBuilder builder = Jwts.builder().header().add(TYP, JWT)
|
||||
.and().issuer(issuer).audience().add(audience)
|
||||
.and().signWith(signingKey);
|
||||
|
||||
// 设置JWT参数
|
||||
kv.forEach(builder::claim);
|
||||
|
||||
// 设置Token过期时间
|
||||
builder.expiration(Date.from(exp)).notBefore(Date.from(now));
|
||||
|
||||
// 组装Token信息
|
||||
TokenInfo tokenInfo = new TokenInfo();
|
||||
tokenInfo.setToken(builder.compact());
|
||||
tokenInfo.setExpire(expireSeconds);
|
||||
|
||||
// 返回Token信息
|
||||
return tokenInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认过期时间(次日凌晨3点)
|
||||
*
|
||||
* @return expire
|
||||
*/
|
||||
public static int getExpire() {
|
||||
LocalTime threeAM = LocalTime.of(3, 0);
|
||||
LocalDate tomorrow = LocalDate.now(ZoneId.systemDefault()).plusDays(1);
|
||||
Instant threeAMTomorrow = tomorrow.atTime(threeAM).atZone(ZoneId.systemDefault()).toInstant();
|
||||
return (int) ChronoUnit.SECONDS.between(Instant.now(), threeAMTomorrow);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取请求头中的客户端id
|
||||
*/
|
||||
public static String getClientId() {
|
||||
String[] tokens = extractAndDecodeAuthorization();
|
||||
assert tokens.length == 2;
|
||||
return tokens[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求头中的客户端密钥
|
||||
*/
|
||||
public static String getClientSecret() {
|
||||
String[] tokens = extractAndDecodeAuthorization();
|
||||
assert tokens.length == 2;
|
||||
return tokens[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户端信息解码
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static String[] extractAndDecodeAuthorization() {
|
||||
// 获取请求头客户端信息
|
||||
String header = Objects.requireNonNull(WebUtil.getRequest()).getHeader(SecureConstant.BASIC_HEADER_KEY);
|
||||
header = Func.toStr(header).replace(SecureConstant.BASIC_HEADER_PREFIX_EXT, SecureConstant.BASIC_HEADER_PREFIX);
|
||||
if (!header.startsWith(SecureConstant.BASIC_HEADER_PREFIX)) {
|
||||
throw new SecurityException(AUTHORIZATION_NOT_FOUND);
|
||||
}
|
||||
byte[] base64Token = header.substring(6).getBytes(Charsets.UTF_8_NAME);
|
||||
|
||||
byte[] decoded;
|
||||
try {
|
||||
decoded = Base64.getDecoder().decode(base64Token);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw new SecurityException(CLIENT_TOKEN_PARSE_FAILED);
|
||||
}
|
||||
|
||||
String token = new String(decoded, Charsets.UTF_8_NAME);
|
||||
int index = token.indexOf(StringPool.COLON);
|
||||
if (index == -1) {
|
||||
throw new SecurityException(INVALID_CLIENT_TOKEN);
|
||||
} else {
|
||||
return new String[]{token.substring(0, index), token.substring(index + 1)};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user