This commit is contained in:
ytChen 2024-03-12 17:09:47 +08:00
parent e3aa1b0fd0
commit de5d9ea54c
17 changed files with 399 additions and 267 deletions

View File

@ -2,6 +2,7 @@ package com.recovery.admin.boot.config;
import com.recovery.admin.boot.interceptor.JwtInterceptor;
import com.recovery.common.base.config.redis.RedisCache;
import com.recovery.common.base.util.RedisUtil;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
@ -13,17 +14,16 @@ import javax.annotation.Resource;
@Configuration
public class InterceptConfig implements WebMvcConfigurer {
@Resource
RedisUtil redisUtil;
RedisCache redisCache;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//添加拦截器
registry.addInterceptor(new JwtInterceptor(redisUtil))
registry.addInterceptor(new JwtInterceptor(redisCache))
//拦截的路径 需要进行token验证的路径
.addPathPatterns("/**")
//放行的路径
.excludePathPatterns("/api/rest/users/getUserByUsername")
.excludePathPatterns("/api/user/sendMsg")
.excludePathPatterns("/api/user/loginPhone")
//放行swagger 测试验证
.excludePathPatterns("/api/user/get");
}

View File

@ -1,7 +1,9 @@
package com.recovery.admin.boot.controller;
import cn.dev33.satoken.stp.StpUtil;
import com.recovery.admin.boot.service.ISysUserService;
import com.recovery.common.base.constant.Constants;
import com.recovery.common.base.dto.UserAuthDTO;
import com.recovery.common.base.result.ApiResult;
import com.recovery.common.base.util.HspHostUtil;
@ -33,7 +35,21 @@ public class testController {
public ApiResult<UserAuthDTO> cs(@RequestParam String name, HttpServletRequest request) {
UserAuthDTO authDTO = iSysUserService.getByUsername(name);
log.info("测试库:"+authDTO.getStatus());
log.info(redisUtil.get("123").toString());
String user = StpUtil.getLoginIdDefaultNull()+"";
StpUtil.kickoutByTokenValue(Constants.LOGIN_USRE_TOKEN);
// StpUtil.logout();
return ApiResult.ok(authDTO);
}
/**
* cs
*/
@GetMapping("/cs1")
public ApiResult ccss(HttpServletRequest request) {
String user = StpUtil.getLoginIdDefaultNull()+"";
StpUtil.kickout(StpUtil.getLoginId());
// StpUtil.logout();
return ApiResult.ok("踢下线");
}
}

View File

@ -1,6 +1,8 @@
package com.recovery.admin.boot.exception;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.util.SaResult;
import com.recovery.common.base.result.ApiResult;
import com.recovery.common.base.result.ResultCode;
import lombok.extern.slf4j.Slf4j;
@ -28,6 +30,45 @@ public class GlobalExceptionHandler {
return ApiResult.failed(ResultCode.SYSTEM_EXECUTION_ERROR.getCode(), e.getMessage());
}
// 全局异常拦截拦截项目中的NotLoginException异常
@ExceptionHandler(NotLoginException.class)
public ApiResult handlerNotLoginException(NotLoginException nle)
throws Exception {
// 打印堆栈以供调试
nle.printStackTrace();
// 判断场景值定制化异常信息
String message = "";
if(nle.getType().equals(NotLoginException.NOT_TOKEN)) {
message = "未能读取到有效 token";
}
else if(nle.getType().equals(NotLoginException.INVALID_TOKEN)) {
message = "token 无效";
}
else if(nle.getType().equals(NotLoginException.TOKEN_TIMEOUT)) {
message = "token 已过期";
}
else if(nle.getType().equals(NotLoginException.BE_REPLACED)) {
message = "token 已被顶下线";
}
else if(nle.getType().equals(NotLoginException.KICK_OUT)) {
message = "token 已被踢下线";
}
else if(nle.getType().equals(NotLoginException.TOKEN_FREEZE)) {
message = "token 已被冻结";
}
else if(nle.getType().equals(NotLoginException.NO_PREFIX)) {
message = "未按照指定前缀提交 token";
}
else {
message = "当前会话未登录";
}
// 返回给前端
return ApiResult.failed(message);
}
//Security
// @ExceptionHandler(value = AccessDeniedException.class)
// public void accessDeniedException(AccessDeniedException e) {

View File

@ -1,11 +1,13 @@
package com.recovery.admin.boot.interceptor;
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.recovery.admin.boot.exception.BusinessException;
import com.recovery.admin.boot.filter.RequestWrapper;
import com.recovery.common.base.config.redis.RedisCache;
import com.recovery.common.base.constant.Constants;
import com.recovery.common.base.result.ResultCode;
import com.recovery.common.base.util.RedisUtil;
@ -27,10 +29,10 @@ import java.util.HashMap;
public class JwtInterceptor implements HandlerInterceptor {
@Resource
RedisUtil redisUtil;
RedisCache redisCache;
public JwtInterceptor(RedisUtil redisUtil) {
this.redisUtil = redisUtil;
public JwtInterceptor(RedisCache redisCache) {
this.redisCache = redisCache;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
@ -45,7 +47,10 @@ public class JwtInterceptor implements HandlerInterceptor {
if (StringUtils.isEmpty(token)) {
throw new BusinessException(ResultCode.LOGIN_ERROR);
}
boolean rest = redisUtil.hasKey("userToken:" +token);
if (!StpUtil.isLogin()) {
throw new BusinessException(ResultCode.IS_LOGIN_EXPIRE_ERROR);
}
boolean rest = redisCache.isExists(Constants.REDIS_USRE_TOKEN+token);
if (!rest) {
throw new BusinessException(ResultCode.LOGIN_EXPIRE_ERROR);
}
@ -53,7 +58,8 @@ public class JwtInterceptor implements HandlerInterceptor {
request.setAttribute(Constants.LOGIN_USRE_TOKEN,token);
try {
//如果验证成功放行请求
DecodedJWT verify = JwtUtils.verifyToken(token);
// DecodedJWT verify = JwtUtils.verifyToken(token);
return true;
}
catch (Exception exception)

View File

@ -59,7 +59,7 @@ public class WebLogAspect {
//切割获取访问目标模块
String[] split = path.split("/");
String module = split[0];
System.out.println(RedisUtils.getDBInfoByHostAndModule(stringRedisTemplate,hospitalHost,"admin"));
log.info(RedisUtils.getDBInfoByHostAndModule(stringRedisTemplate,hospitalHost,"admin")+"");
//根据域名和请求的模块名查询目标数据库
HttpSession session = request.getSession();
/**
@ -71,6 +71,8 @@ public class WebLogAspect {
//执行完切面后将线程共享中的数据源名称清空
@After("webLog()")
public void after(JoinPoint joinPoint){
//清除
HspHostUtil.clear();
DataSourceContextHolder.clearDBType();
}
}

View File

@ -2,7 +2,10 @@ package com.recovery.auth.config;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.jwt.StpLogicJwtForMixin;
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
import cn.dev33.satoken.same.SaSameUtil;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.util.SaResult;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -29,4 +32,10 @@ public class SaTokenConfigure implements WebMvcConfigurer {
})
;
}
// Sa-Token 整合 jwt (Simple 简单模式)
@Bean
public StpLogic getStpLogicJwt() {
return new StpLogicJwtForSimple();
}
}

View File

@ -8,10 +8,14 @@ import com.recovery.auth.exception.BusinessException;
import com.recovery.auth.feign.UserFeignClient;
import com.recovery.auth.security.details.user.JwtAuthenticationRequest;
import com.recovery.auth.service.AuthService;
import com.recovery.common.base.config.redis.RedisCache;
import com.recovery.common.base.constant.Constants;
import com.recovery.common.base.dto.UserAuthDTO;
import com.recovery.common.base.dto.UserAuthorityDto;
import com.recovery.common.base.result.ApiResult;
import com.recovery.common.base.result.ResultCode;
import com.recovery.common.base.util.EncryptUtil;
import com.recovery.common.base.util.RedisUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
@ -34,6 +38,8 @@ public class AuthController {
AuthService authService;
@Resource
UserFeignClient userFeignClient;
@Resource
RedisCache redisCache;
@PostMapping("/token")
public ApiResult postAccessToken(@RequestBody JwtAuthenticationRequest authenticationRequest, HttpServletRequest request){
@ -48,8 +54,8 @@ public class AuthController {
return ApiResult.ok(map);
}
@GetMapping("/doLogin")
public SaResult doLogin(@RequestBody JwtAuthenticationRequest authenticationRequest) {
@PostMapping("/doLogin")
public ApiResult doLogin(@RequestBody JwtAuthenticationRequest authenticationRequest) {
if(StringUtils.isEmpty(authenticationRequest.getUsername())){
throw new BusinessException("账户不能为空");
}
@ -62,16 +68,25 @@ public class AuthController {
if (ResultCode.SUCCESS.getCode().equals(result.getCode())) {
userDetails = result.getData();
}
// 此处仅作模拟示例真实项目需要从数据库中查询数据进行比对
if (userDetails.getUserName().equals(authenticationRequest.getUsername()) && userDetails.getPassword().equals(authenticationRequest.getPassword())) {
// 数据库中查询数据进行比对
if (userDetails.getUserName().equals(authenticationRequest.getUsername()) && userDetails.getPassword().equals(EncryptUtil.encrypt(authenticationRequest.getPassword()))) {
log.info("密码校验成功!");
StpUtil.login(userDetails.getUserName());
StpUtil.login(userDetails.getUserId(),"PC");
}else {
return SaResult.error("密码错误");
return ApiResult.failed("密码错误");
}
//认证通过 使用userid 储存用户信息
try {
redisCache.put_obj(Constants.REDIS_USRE_INFO + userDetails.getUserId(),userDetails , Constants.REDIS_TOKEN_TIME);
} catch (Exception e) {
log.error("redis储存token报错"+e.getMessage(),e);
}
// 第3步返回给前端
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
return SaResult.ok("登录成功").setData(tokenInfo);
HashMap map= new HashMap();
map.put("token",tokenInfo.getTokenValue());
map.put("user",userDetails);
return ApiResult.ok(map);
}
// @GetMapping("/public-key")

View File

@ -9,8 +9,8 @@ spring:
cloud:
nacos:
discovery:
# metadata:
# serviceGroup: ytChen
metadata:
serviceGroup: ytChen
server-addr: localhost:8848
namespace: 11bfd099-10d6-4f2c-b969-58b76e435cce
config:
@ -21,3 +21,15 @@ spring:
namespace: 11bfd099-10d6-4f2c-b969-58b76e435cce
server:
port: 9001
sa-token:
# jwt秘钥
jwt-secret-key: asdasdasifhueuiwyurfewbfjsdafjk123
token-name: satoken
timeout: -1
active-timeout: -1
is-concurrent: false
is-share: false
is-log: true

View File

@ -22,7 +22,17 @@ public class Constants {
public final static String UPLOAD_SIZE_ERR_MSG = "上传大小错误";
public static final String LOGIN_USRE_TOKEN = "x-userToken";
public static final String LOGIN_USRE_TOKEN = "satoken";
//token
public static final String REDIS_USRE_TOKEN = "satoken:login:token:";
//用户信息
public static final String REDIS_USRE_INFO = "satoken:login:userInfo:";
//token过期时间 七天
public static final Integer REDIS_TOKEN_TIME = 7*24*60*60;
public final static String WHOLE_DOMAIN_REDIS_KEY = "HOE_WHOLE_DOMAIN_REDIS_KEY";

View File

@ -22,6 +22,7 @@ public enum ResultCode implements IResultCode, Serializable {
LOGIN_ERROR("1001", "未登录"),
LOGIN_EXPIRE_ERROR("1002", "登录过期,请重新登录!"),
IS_LOGIN_EXPIRE_ERROR("1003", "您已被踢下线,请重新登录!"),
SYSTEM_EXECUTION_ERROR("999999", "系统执行出错"),
USERNAME_OR_PASSWORD_ERROR("A00100", "用户名或密码错误"),
USER_NOT_EXIST("A00101", "用户不存在"),

View File

@ -28,6 +28,9 @@ public class HspHostUtil {
public static void setHspHost(String hspHost) {
HSP_HSOT.set(hspHost);
}
public static void clear() {
HSP_HSOT.remove();
}
/**

View File

@ -54,13 +54,11 @@
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<!-- Sa-Token 权限认证Reactor响应式集成在线文档https://sa-token.cc -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-reactor-spring-boot-starter</artifactId>
<version>1.34.0</version>
<version>1.37.0</version>
</dependency>

View File

@ -5,6 +5,7 @@ import cn.dev33.satoken.reactor.filter.SaReactorFilter;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -12,31 +13,55 @@ import org.springframework.context.annotation.Configuration;
* [Sa-Token 权限认证] 配置类
*/
@Configuration
@Slf4j
public class SaTokenConfigure {
// 注册 Sa-Token全局过滤器
/**
* 注册 [Sa-Token全局过滤器]
*/
@Bean
public SaReactorFilter getSaReactorFilter() {
return new SaReactorFilter()
// 拦截地址
.addInclude("/**") /* 拦截全部path */
// 开放地址
// 指定 [拦截路由]
.addInclude("/**") /* 拦截所有path */
// 指定 [放行路由]
.addExclude("/favicon.ico")
// 鉴权方法每次访问进入
// 指定[认证函数]: 每次请求执行
.setAuth(obj -> {
// 登录校验 -- 拦截所有路由并排除/user/doLogin 用于开放登录
SaRouter.match("/**", "/auth/oauth/doLogin", r -> StpUtil.checkLogin());
// 权限认证 -- 不同模块, 校验不同权限
SaRouter.match("/api/test1", r -> StpUtil.checkPermission("api.test1"));
SaRouter.match("/api/test2", r -> StpUtil.checkPermission("api.test2"));
SaRouter.match("/api/test3", r -> StpUtil.checkRoleOr("admin", "super"));
// 更多匹配 ... */
log.info("---------- sa全局认证");
// SaRouter.match("/test/test", () -> StpUtil.checkLogin());
})
// 异常处理方法每次setAuth函数出现异常时进入
// 指定[异常处理函数]每次[认证函数]发生异常时执行此函数
.setError(e -> {
log.info("---------- sa全局异常 ");
return SaResult.error(e.getMessage());
})
;
}
// 注册 Sa-Token全局过滤器
// @Bean
// public SaReactorFilter getSaReactorFilter() {
// return new SaReactorFilter()
// // 拦截地址
// .addInclude("/**") /* 拦截全部path */
// // 开放地址
// .addExclude("/favicon.ico")
// // 鉴权方法每次访问进入
// .setAuth(obj -> {
// // 登录校验 -- 拦截所有路由并排除/user/doLogin 用于开放登录
// SaRouter.match("/**", "/auth/oauth/doLogin", r -> StpUtil.checkLogin());
//
// // 权限认证 -- 不同模块, 校验不同权限
// SaRouter.match("/api/test1", r -> StpUtil.checkPermission("api.test1"));
// SaRouter.match("/api/test2", r -> StpUtil.checkPermission("api.test2"));
// SaRouter.match("/api/test3", r -> StpUtil.checkRoleOr("admin", "super"));
//
// // 更多匹配 ... */
// })
// // 异常处理方法每次setAuth函数出现异常时进入
// .setError(e -> {
// return SaResult.error(e.getMessage());
// })
// ;
// }
}

View File

@ -1,80 +1,80 @@
//package com.recovery.gateway.security;
//
//
//import com.recovery.common.base.constant.SecurityConstants;
//import com.recovery.common.base.result.ResultCode;
//import com.recovery.gateway.util.ResponseUtils;
//import lombok.AllArgsConstructor;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.core.convert.converter.Converter;
//import org.springframework.security.authentication.AbstractAuthenticationToken;
//import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
//import org.springframework.security.config.web.server.ServerHttpSecurity;
//import org.springframework.security.oauth2.jwt.Jwt;
//import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
//import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
//import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
//import org.springframework.security.web.server.SecurityWebFilterChain;
//import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
//import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
//import reactor.core.publisher.Mono;
///**
// * @author
// */
//@AllArgsConstructor
//@Configuration
//@EnableWebFluxSecurity
//public class ResourceServerConfig {
//
// private final ResourceServerManager resourceServerManager;
//
// @Bean
// public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
// http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(jwtAuthenticationConverter());
// http.oauth2ResourceServer().authenticationEntryPoint(authenticationEntryPoint());
// http.authorizeExchange()
// .anyExchange().access(resourceServerManager)
// .and()
// .exceptionHandling()
// .accessDeniedHandler(accessDeniedHandler()) // 处理未授权
// .authenticationEntryPoint(authenticationEntryPoint()) //处理未认证
// .and().csrf().disable();
// return http.build();
// }
//
// /**
// * 自定义未授权响应
// */
// @Bean
// ServerAccessDeniedHandler accessDeniedHandler() {
// return (exchange, denied) -> {
// Mono<Void> mono = Mono.defer(() -> Mono.just(exchange.getResponse()))
// .flatMap(response -> ResponseUtils.writeErrorInfo(response, ResultCode.ACCESS_UNAUTHORIZED));
// return mono;
// };
// }
//
// /**
// * token无效或者已过期自定义响应
// */
// @Bean
// ServerAuthenticationEntryPoint authenticationEntryPoint() {
// return (exchange, e) -> {
// Mono<Void> mono = Mono.defer(() -> Mono.just(exchange.getResponse()))
// .flatMap(response -> ResponseUtils.writeErrorInfo(response, ResultCode.TOKEN_INVALID_OR_EXPIRED));
// return mono;
// };
// }
//
// @Bean
// public Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter() {
// JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
// jwtGrantedAuthoritiesConverter.setAuthorityPrefix(SecurityConstants.AUTHORITY_PREFIX);
// jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(SecurityConstants.JWT_AUTHORITIES_KEY);
//
// JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
// jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
// return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
// }
//}
package com.recovery.gateway.security;
import com.recovery.common.base.constant.SecurityConstants;
import com.recovery.common.base.result.ResultCode;
import com.recovery.gateway.util.ResponseUtils;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
import reactor.core.publisher.Mono;
/**
* @author
*/
@AllArgsConstructor
@Configuration
@EnableWebFluxSecurity
public class ResourceServerConfig {
private final ResourceServerManager resourceServerManager;
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(jwtAuthenticationConverter());
http.oauth2ResourceServer().authenticationEntryPoint(authenticationEntryPoint());
http.authorizeExchange()
.anyExchange().access(resourceServerManager)
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler()) // 处理未授权
.authenticationEntryPoint(authenticationEntryPoint()) //处理未认证
.and().csrf().disable();
return http.build();
}
/**
* 自定义未授权响应
*/
@Bean
ServerAccessDeniedHandler accessDeniedHandler() {
return (exchange, denied) -> {
Mono<Void> mono = Mono.defer(() -> Mono.just(exchange.getResponse()))
.flatMap(response -> ResponseUtils.writeErrorInfo(response, ResultCode.ACCESS_UNAUTHORIZED));
return mono;
};
}
/**
* token无效或者已过期自定义响应
*/
@Bean
ServerAuthenticationEntryPoint authenticationEntryPoint() {
return (exchange, e) -> {
Mono<Void> mono = Mono.defer(() -> Mono.just(exchange.getResponse()))
.flatMap(response -> ResponseUtils.writeErrorInfo(response, ResultCode.TOKEN_INVALID_OR_EXPIRED));
return mono;
};
}
@Bean
public Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
jwtGrantedAuthoritiesConverter.setAuthorityPrefix(SecurityConstants.AUTHORITY_PREFIX);
jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(SecurityConstants.JWT_AUTHORITIES_KEY);
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
}
}

View File

@ -1,79 +1,79 @@
//package com.recovery.gateway.security;
//
//import cn.hutool.core.collection.CollectionUtil;
//import cn.hutool.core.convert.Convert;
//import cn.hutool.core.util.StrUtil;
//
//import com.recovery.common.base.constant.GlobalConstants;
//import com.recovery.common.base.constant.SecurityConstants;
//import com.recovery.gateway.util.UrlPatternUtils;
//import lombok.RequiredArgsConstructor;
//import lombok.Setter;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.boot.context.properties.ConfigurationProperties;
//import org.springframework.data.redis.core.RedisTemplate;
//import org.springframework.http.HttpMethod;
//import org.springframework.http.server.reactive.ServerHttpRequest;
//import org.springframework.security.authorization.AuthorizationDecision;
//import org.springframework.security.authorization.ReactiveAuthorizationManager;
//import org.springframework.security.core.Authentication;
//import org.springframework.security.core.GrantedAuthority;
//import org.springframework.security.web.server.authorization.AuthorizationContext;
//import org.springframework.stereotype.Component;
//import org.springframework.util.AntPathMatcher;
//import org.springframework.util.PathMatcher;
//import reactor.core.publisher.Mono;
//
//import java.util.ArrayList;
//import java.util.List;
//import java.util.Map;
///**
// * Created with IntelliJ IDEA.
// *
// * @author
// * @date 2021/11/24
// * @description
// * @modifiedBy
// * @version: 1.0
// */
//@Component
//@RequiredArgsConstructor
//@Slf4j
//@ConfigurationProperties(prefix = "security")
//public class ResourceServerManager implements ReactiveAuthorizationManager<AuthorizationContext> {
// private final RedisTemplate redisTemplate;
//
// @Setter
// private List<String> ignoreUrls;
//
// @Override
// public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
// ServerHttpRequest request = authorizationContext.getExchange().getRequest();
// if (request.getMethod() == HttpMethod.OPTIONS) { // 预检请求放行
// return Mono.just(new AuthorizationDecision(true));
// }
// PathMatcher pathMatcher = new AntPathMatcher();
// String method = request.getMethodValue();
// String path = request.getURI().getPath();
//
// // 跳过token校验放在这里去做是为了能够动态刷新
//// if (skipValid(path)) {
// return Mono.just(new AuthorizationDecision(true));
//// }
// }
//
// /**
// * 跳过校验
// *
// * @param path
// * @return
// */
// private boolean skipValid(String path) {
// for (String skipPath : ignoreUrls) {
// if (UrlPatternUtils.match(skipPath, path)) {
// return true;
// }
// }
// return false;
// }
package com.recovery.gateway.security;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.recovery.common.base.constant.GlobalConstants;
import com.recovery.common.base.constant.SecurityConstants;
import com.recovery.gateway.util.UrlPatternUtils;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Created with IntelliJ IDEA.
*
* @author
* @date 2021/11/24
* @description
* @modifiedBy
* @version: 1.0
*/
@Component
@RequiredArgsConstructor
@Slf4j
@ConfigurationProperties(prefix = "security")
public class ResourceServerManager implements ReactiveAuthorizationManager<AuthorizationContext> {
private final RedisTemplate redisTemplate;
@Setter
private List<String> ignoreUrls;
@Override
public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
ServerHttpRequest request = authorizationContext.getExchange().getRequest();
if (request.getMethod() == HttpMethod.OPTIONS) { // 预检请求放行
return Mono.just(new AuthorizationDecision(true));
}
PathMatcher pathMatcher = new AntPathMatcher();
String method = request.getMethodValue();
String path = request.getURI().getPath();
// 跳过token校验放在这里去做是为了能够动态刷新
// if (skipValid(path)) {
return Mono.just(new AuthorizationDecision(true));
// }
}
/**
* 跳过校验
*
* @param path
* @return
*/
private boolean skipValid(String path) {
for (String skipPath : ignoreUrls) {
if (UrlPatternUtils.match(skipPath, path)) {
return true;
}
}
return false;
}
}

View File

@ -1,69 +1,57 @@
//package com.recovery.gateway.security;
//
//import cn.dev33.satoken.same.SaSameUtil;
//import cn.hutool.core.util.StrUtil;
//
//import com.nimbusds.jose.JWSObject;
//import com.recovery.common.base.constant.SecurityConstants;
//import lombok.RequiredArgsConstructor;
//import lombok.SneakyThrows;
//import lombok.extern.slf4j.Slf4j;
//import org.apache.logging.log4j.util.Strings;
//import org.springframework.cloud.gateway.filter.GatewayFilterChain;
//import org.springframework.cloud.gateway.filter.GlobalFilter;
//import org.springframework.core.Ordered;
//import org.springframework.data.redis.core.RedisTemplate;
//import org.springframework.http.server.reactive.ServerHttpRequest;
//import org.springframework.http.server.reactive.ServerHttpResponse;
//import org.springframework.stereotype.Component;
//import org.springframework.web.server.ServerWebExchange;
//import reactor.core.publisher.Mono;
//
//import java.net.URLEncoder;
///**
// * Created with IntelliJ IDEA.
// *
// * @author AI码师 关注公众号"AI码师"获取完整源码
// * @date 2021/11/24
// * @description
// * @modifiedBy
// * @version: 1.0
// */
//@Component
//@Slf4j
//@RequiredArgsConstructor
//public class SecurityGlobalFilter implements GlobalFilter, Ordered {
//
// @SneakyThrows
// @Override
// public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//
// ServerHttpRequest request = exchange.getRequest();
// log.info("接受到请求地址:"+request.getURI());
// ServerHttpResponse response = exchange.getResponse();
// // 不是正确的的JWT不做解析处理
// String token = request.getHeaders().getFirst(SecurityConstants.AUTHORIZATION_KEY);
// if (StrUtil.isBlank(token) || !StrUtil.startWithIgnoreCase(token, SecurityConstants.JWT_PREFIX)) {
// return chain.filter(exchange);
// }
// // 为请求追加 Same-Token 参数
// request.mutate()
// // 为请求追加 Same-Token 参数
// .header(SaSameUtil.SAME_TOKEN, SaSameUtil.getToken())
// .build();
// //结束
// // 解析JWT获取jti以jti为key判断redis的黑名单列表是否存在存在则拦截访问
// token = StrUtil.replaceIgnoreCase(token, SecurityConstants.JWT_PREFIX, Strings.EMPTY);
// String payload = StrUtil.toString(JWSObject.parse(token).getPayload());
// request = exchange.getRequest().mutate()
// .header(SecurityConstants.JWT_PAYLOAD_KEY, URLEncoder.encode(payload, "UTF-8"))
// .build();
// exchange = exchange.mutate().request(request).build();
// return chain.filter(exchange);
// }
//
// @Override
// public int getOrder() {
// return 0;
// }
//}
package com.recovery.gateway.security;
import cn.dev33.satoken.same.SaSameUtil;
import cn.hutool.core.util.StrUtil;
import com.nimbusds.jose.JWSObject;
import com.recovery.common.base.constant.SecurityConstants;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.net.URLEncoder;
/**
* Created with IntelliJ IDEA.
*
* @author
* @date 2021/11/24
* @description
* @modifiedBy
* @version: 1.0
*/
@Component
@Slf4j
@RequiredArgsConstructor
public class SecurityGlobalFilter implements GlobalFilter, Ordered {
@SneakyThrows
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
log.info("接受到请求地址:"+request.getURI());
ServerHttpResponse response = exchange.getResponse();
// 为请求追加 Same-Token 参数
request.mutate()
// 为请求追加 Same-Token 参数
.header(SaSameUtil.SAME_TOKEN, SaSameUtil.getToken())
.build();
exchange = exchange.mutate().request(request).build();
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}

View File

@ -24,7 +24,7 @@
<spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
<spring-boot.version>2.5.4</spring-boot.version>
<hoe-version>1.0.0</hoe-version>
<hutool-version>5.5.8</hutool-version>
<hutool-version>5.7.14</hutool-version>
<mysql.version>8.0.22</mysql.version>
<druid.version>1.2.4</druid.version>
<mybatis-plus.version>3.4.3</mybatis-plus.version>
@ -52,6 +52,12 @@
<artifactId>sa-token-redis-jackson</artifactId>
<version>1.37.0</version>
</dependency>
<!-- Sa-Token 整合 jwt -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jwt</artifactId>
<version>1.37.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>