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

View File

@ -1,7 +1,9 @@
package com.recovery.admin.boot.controller; package com.recovery.admin.boot.controller;
import cn.dev33.satoken.stp.StpUtil;
import com.recovery.admin.boot.service.ISysUserService; 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.dto.UserAuthDTO;
import com.recovery.common.base.result.ApiResult; import com.recovery.common.base.result.ApiResult;
import com.recovery.common.base.util.HspHostUtil; import com.recovery.common.base.util.HspHostUtil;
@ -33,7 +35,21 @@ public class testController {
public ApiResult<UserAuthDTO> cs(@RequestParam String name, HttpServletRequest request) { public ApiResult<UserAuthDTO> cs(@RequestParam String name, HttpServletRequest request) {
UserAuthDTO authDTO = iSysUserService.getByUsername(name); UserAuthDTO authDTO = iSysUserService.getByUsername(name);
log.info("测试库:"+authDTO.getStatus()); 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); 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; 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.ApiResult;
import com.recovery.common.base.result.ResultCode; import com.recovery.common.base.result.ResultCode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -28,6 +30,45 @@ public class GlobalExceptionHandler {
return ApiResult.failed(ResultCode.SYSTEM_EXECUTION_ERROR.getCode(), e.getMessage()); 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 //Security
// @ExceptionHandler(value = AccessDeniedException.class) // @ExceptionHandler(value = AccessDeniedException.class)
// public void accessDeniedException(AccessDeniedException e) { // public void accessDeniedException(AccessDeniedException e) {

View File

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

View File

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

View File

@ -2,7 +2,10 @@ package com.recovery.auth.config;
import cn.dev33.satoken.context.SaHolder; import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.filter.SaServletFilter; 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.same.SaSameUtil;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.util.SaResult; import cn.dev33.satoken.util.SaResult;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; 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.feign.UserFeignClient;
import com.recovery.auth.security.details.user.JwtAuthenticationRequest; import com.recovery.auth.security.details.user.JwtAuthenticationRequest;
import com.recovery.auth.service.AuthService; 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.UserAuthDTO;
import com.recovery.common.base.dto.UserAuthorityDto; import com.recovery.common.base.dto.UserAuthorityDto;
import com.recovery.common.base.result.ApiResult; import com.recovery.common.base.result.ApiResult;
import com.recovery.common.base.result.ResultCode; 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.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -34,6 +38,8 @@ public class AuthController {
AuthService authService; AuthService authService;
@Resource @Resource
UserFeignClient userFeignClient; UserFeignClient userFeignClient;
@Resource
RedisCache redisCache;
@PostMapping("/token") @PostMapping("/token")
public ApiResult postAccessToken(@RequestBody JwtAuthenticationRequest authenticationRequest, HttpServletRequest request){ public ApiResult postAccessToken(@RequestBody JwtAuthenticationRequest authenticationRequest, HttpServletRequest request){
@ -48,8 +54,8 @@ public class AuthController {
return ApiResult.ok(map); return ApiResult.ok(map);
} }
@GetMapping("/doLogin") @PostMapping("/doLogin")
public SaResult doLogin(@RequestBody JwtAuthenticationRequest authenticationRequest) { public ApiResult doLogin(@RequestBody JwtAuthenticationRequest authenticationRequest) {
if(StringUtils.isEmpty(authenticationRequest.getUsername())){ if(StringUtils.isEmpty(authenticationRequest.getUsername())){
throw new BusinessException("账户不能为空"); throw new BusinessException("账户不能为空");
} }
@ -62,16 +68,25 @@ public class AuthController {
if (ResultCode.SUCCESS.getCode().equals(result.getCode())) { if (ResultCode.SUCCESS.getCode().equals(result.getCode())) {
userDetails = result.getData(); 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("密码校验成功!"); log.info("密码校验成功!");
StpUtil.login(userDetails.getUserName()); StpUtil.login(userDetails.getUserId(),"PC");
}else { }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步返回给前端 // 第3步返回给前端
SaTokenInfo tokenInfo = StpUtil.getTokenInfo(); 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") // @GetMapping("/public-key")

View File

@ -9,8 +9,8 @@ spring:
cloud: cloud:
nacos: nacos:
discovery: discovery:
# metadata: metadata:
# serviceGroup: ytChen serviceGroup: ytChen
server-addr: localhost:8848 server-addr: localhost:8848
namespace: 11bfd099-10d6-4f2c-b969-58b76e435cce namespace: 11bfd099-10d6-4f2c-b969-58b76e435cce
config: config:
@ -21,3 +21,15 @@ spring:
namespace: 11bfd099-10d6-4f2c-b969-58b76e435cce namespace: 11bfd099-10d6-4f2c-b969-58b76e435cce
server: server:
port: 9001 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 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"; 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_ERROR("1001", "未登录"),
LOGIN_EXPIRE_ERROR("1002", "登录过期,请重新登录!"), LOGIN_EXPIRE_ERROR("1002", "登录过期,请重新登录!"),
IS_LOGIN_EXPIRE_ERROR("1003", "您已被踢下线,请重新登录!"),
SYSTEM_EXECUTION_ERROR("999999", "系统执行出错"), SYSTEM_EXECUTION_ERROR("999999", "系统执行出错"),
USERNAME_OR_PASSWORD_ERROR("A00100", "用户名或密码错误"), USERNAME_OR_PASSWORD_ERROR("A00100", "用户名或密码错误"),
USER_NOT_EXIST("A00101", "用户不存在"), USER_NOT_EXIST("A00101", "用户不存在"),

View File

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

View File

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

View File

@ -5,6 +5,7 @@ import cn.dev33.satoken.reactor.filter.SaReactorFilter;
import cn.dev33.satoken.router.SaRouter; import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult; import cn.dev33.satoken.util.SaResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -12,31 +13,55 @@ import org.springframework.context.annotation.Configuration;
* [Sa-Token 权限认证] 配置类 * [Sa-Token 权限认证] 配置类
*/ */
@Configuration @Configuration
@Slf4j
public class SaTokenConfigure { public class SaTokenConfigure {
// 注册 Sa-Token全局过滤器
/**
* 注册 [Sa-Token全局过滤器]
*/
@Bean @Bean
public SaReactorFilter getSaReactorFilter() { public SaReactorFilter getSaReactorFilter() {
return new SaReactorFilter() return new SaReactorFilter()
// 拦截地址 // 指定 [拦截路由]
.addInclude("/**") /* 拦截全部path */ .addInclude("/**") /* 拦截所有path */
// 开放地址 // 指定 [放行路由]
.addExclude("/favicon.ico") .addExclude("/favicon.ico")
// 鉴权方法每次访问进入 // 指定[认证函数]: 每次请求执行
.setAuth(obj -> { .setAuth(obj -> {
// 登录校验 -- 拦截所有路由并排除/user/doLogin 用于开放登录 log.info("---------- sa全局认证");
SaRouter.match("/**", "/auth/oauth/doLogin", r -> StpUtil.checkLogin()); // SaRouter.match("/test/test", () -> 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 -> { .setError(e -> {
log.info("---------- sa全局异常 ");
return SaResult.error(e.getMessage()); 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; package com.recovery.gateway.security;
//
//
//import com.recovery.common.base.constant.SecurityConstants; import com.recovery.common.base.constant.SecurityConstants;
//import com.recovery.common.base.result.ResultCode; import com.recovery.common.base.result.ResultCode;
//import com.recovery.gateway.util.ResponseUtils; import com.recovery.gateway.util.ResponseUtils;
//import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
//import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
//import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
//import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.authentication.AbstractAuthenticationToken;
//import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
//import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity;
//import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.Jwt;
//import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; 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.JwtGrantedAuthoritiesConverter;
//import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter; import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
//import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.SecurityWebFilterChain;
//import org.springframework.security.web.server.ServerAuthenticationEntryPoint; import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
//import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler; import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
//import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
///** /**
// * @author * @author
// */ */
//@AllArgsConstructor @AllArgsConstructor
//@Configuration @Configuration
//@EnableWebFluxSecurity @EnableWebFluxSecurity
//public class ResourceServerConfig { public class ResourceServerConfig {
//
// private final ResourceServerManager resourceServerManager; private final ResourceServerManager resourceServerManager;
//
// @Bean @Bean
// public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
// http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(jwtAuthenticationConverter()); http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(jwtAuthenticationConverter());
// http.oauth2ResourceServer().authenticationEntryPoint(authenticationEntryPoint()); http.oauth2ResourceServer().authenticationEntryPoint(authenticationEntryPoint());
// http.authorizeExchange() http.authorizeExchange()
// .anyExchange().access(resourceServerManager) .anyExchange().access(resourceServerManager)
// .and() .and()
// .exceptionHandling() .exceptionHandling()
// .accessDeniedHandler(accessDeniedHandler()) // 处理未授权 .accessDeniedHandler(accessDeniedHandler()) // 处理未授权
// .authenticationEntryPoint(authenticationEntryPoint()) //处理未认证 .authenticationEntryPoint(authenticationEntryPoint()) //处理未认证
// .and().csrf().disable(); .and().csrf().disable();
// return http.build(); return http.build();
// } }
//
// /** /**
// * 自定义未授权响应 * 自定义未授权响应
// */ */
// @Bean @Bean
// ServerAccessDeniedHandler accessDeniedHandler() { ServerAccessDeniedHandler accessDeniedHandler() {
// return (exchange, denied) -> { return (exchange, denied) -> {
// Mono<Void> mono = Mono.defer(() -> Mono.just(exchange.getResponse())) Mono<Void> mono = Mono.defer(() -> Mono.just(exchange.getResponse()))
// .flatMap(response -> ResponseUtils.writeErrorInfo(response, ResultCode.ACCESS_UNAUTHORIZED)); .flatMap(response -> ResponseUtils.writeErrorInfo(response, ResultCode.ACCESS_UNAUTHORIZED));
// return mono; return mono;
// }; };
// } }
//
// /** /**
// * token无效或者已过期自定义响应 * token无效或者已过期自定义响应
// */ */
// @Bean @Bean
// ServerAuthenticationEntryPoint authenticationEntryPoint() { ServerAuthenticationEntryPoint authenticationEntryPoint() {
// return (exchange, e) -> { return (exchange, e) -> {
// Mono<Void> mono = Mono.defer(() -> Mono.just(exchange.getResponse())) Mono<Void> mono = Mono.defer(() -> Mono.just(exchange.getResponse()))
// .flatMap(response -> ResponseUtils.writeErrorInfo(response, ResultCode.TOKEN_INVALID_OR_EXPIRED)); .flatMap(response -> ResponseUtils.writeErrorInfo(response, ResultCode.TOKEN_INVALID_OR_EXPIRED));
// return mono; return mono;
// }; };
// } }
//
// @Bean @Bean
// public Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter() { public Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter() {
// JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
// jwtGrantedAuthoritiesConverter.setAuthorityPrefix(SecurityConstants.AUTHORITY_PREFIX); jwtGrantedAuthoritiesConverter.setAuthorityPrefix(SecurityConstants.AUTHORITY_PREFIX);
// jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(SecurityConstants.JWT_AUTHORITIES_KEY); jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(SecurityConstants.JWT_AUTHORITIES_KEY);
//
// JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter(); JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
// jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter); jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
// return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter); return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
// } }
//} }

View File

@ -1,79 +1,79 @@
//package com.recovery.gateway.security; package com.recovery.gateway.security;
//
//import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
//import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
//import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
//
//import com.recovery.common.base.constant.GlobalConstants; import com.recovery.common.base.constant.GlobalConstants;
//import com.recovery.common.base.constant.SecurityConstants; import com.recovery.common.base.constant.SecurityConstants;
//import com.recovery.gateway.util.UrlPatternUtils; import com.recovery.gateway.util.UrlPatternUtils;
//import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
//import lombok.Setter; import lombok.Setter;
//import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
//import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
//import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
//import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
//import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequest;
//import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationDecision;
//import org.springframework.security.authorization.ReactiveAuthorizationManager; import org.springframework.security.authorization.ReactiveAuthorizationManager;
//import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
//import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
//import org.springframework.security.web.server.authorization.AuthorizationContext; import org.springframework.security.web.server.authorization.AuthorizationContext;
//import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
//import org.springframework.util.AntPathMatcher; import org.springframework.util.AntPathMatcher;
//import org.springframework.util.PathMatcher; import org.springframework.util.PathMatcher;
//import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
//
//import java.util.ArrayList; import java.util.ArrayList;
//import java.util.List; import java.util.List;
//import java.util.Map; import java.util.Map;
///** /**
// * Created with IntelliJ IDEA. * Created with IntelliJ IDEA.
// * *
// * @author * @author
// * @date 2021/11/24 * @date 2021/11/24
// * @description * @description
// * @modifiedBy * @modifiedBy
// * @version: 1.0 * @version: 1.0
// */ */
//@Component @Component
//@RequiredArgsConstructor @RequiredArgsConstructor
//@Slf4j @Slf4j
//@ConfigurationProperties(prefix = "security") @ConfigurationProperties(prefix = "security")
//public class ResourceServerManager implements ReactiveAuthorizationManager<AuthorizationContext> { public class ResourceServerManager implements ReactiveAuthorizationManager<AuthorizationContext> {
// private final RedisTemplate redisTemplate; private final RedisTemplate redisTemplate;
//
// @Setter @Setter
// private List<String> ignoreUrls; private List<String> ignoreUrls;
//
// @Override @Override
// public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) { public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
// ServerHttpRequest request = authorizationContext.getExchange().getRequest(); ServerHttpRequest request = authorizationContext.getExchange().getRequest();
// if (request.getMethod() == HttpMethod.OPTIONS) { // 预检请求放行 if (request.getMethod() == HttpMethod.OPTIONS) { // 预检请求放行
// return Mono.just(new AuthorizationDecision(true)); return Mono.just(new AuthorizationDecision(true));
// } }
// PathMatcher pathMatcher = new AntPathMatcher(); PathMatcher pathMatcher = new AntPathMatcher();
// String method = request.getMethodValue(); String method = request.getMethodValue();
// String path = request.getURI().getPath(); String path = request.getURI().getPath();
//
// // 跳过token校验放在这里去做是为了能够动态刷新 // 跳过token校验放在这里去做是为了能够动态刷新
//// if (skipValid(path)) { // if (skipValid(path)) {
// return Mono.just(new AuthorizationDecision(true)); 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;
// }
// } // }
}
/**
* 跳过校验
*
* @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; package com.recovery.gateway.security;
//
//import cn.dev33.satoken.same.SaSameUtil; import cn.dev33.satoken.same.SaSameUtil;
//import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
//
//import com.nimbusds.jose.JWSObject; import com.nimbusds.jose.JWSObject;
//import com.recovery.common.base.constant.SecurityConstants; import com.recovery.common.base.constant.SecurityConstants;
//import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
//import lombok.SneakyThrows; import lombok.SneakyThrows;
//import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
//import org.apache.logging.log4j.util.Strings; import org.apache.logging.log4j.util.Strings;
//import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GatewayFilterChain;
//import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.GlobalFilter;
//import org.springframework.core.Ordered; import org.springframework.core.Ordered;
//import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
//import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequest;
//import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.http.server.reactive.ServerHttpResponse;
//import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
//import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
//import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
//
//import java.net.URLEncoder; import java.net.URLEncoder;
///** /**
// * Created with IntelliJ IDEA. * Created with IntelliJ IDEA.
// * *
// * @author AI码师 关注公众号"AI码师"获取完整源码 * @author
// * @date 2021/11/24 * @date 2021/11/24
// * @description * @description
// * @modifiedBy * @modifiedBy
// * @version: 1.0 * @version: 1.0
// */ */
//@Component @Component
//@Slf4j @Slf4j
//@RequiredArgsConstructor @RequiredArgsConstructor
//public class SecurityGlobalFilter implements GlobalFilter, Ordered { public class SecurityGlobalFilter implements GlobalFilter, Ordered {
//
// @SneakyThrows @SneakyThrows
// @Override @Override
// public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//
// ServerHttpRequest request = exchange.getRequest(); ServerHttpRequest request = exchange.getRequest();
// log.info("接受到请求地址:"+request.getURI()); log.info("接受到请求地址:"+request.getURI());
// ServerHttpResponse response = exchange.getResponse(); ServerHttpResponse response = exchange.getResponse();
// // 不是正确的的JWT不做解析处理 // 为请求追加 Same-Token 参数
// String token = request.getHeaders().getFirst(SecurityConstants.AUTHORIZATION_KEY); request.mutate()
// if (StrUtil.isBlank(token) || !StrUtil.startWithIgnoreCase(token, SecurityConstants.JWT_PREFIX)) { // 为请求追加 Same-Token 参数
// return chain.filter(exchange); .header(SaSameUtil.SAME_TOKEN, SaSameUtil.getToken())
// } .build();
// // 为请求追加 Same-Token 参数 exchange = exchange.mutate().request(request).build();
// request.mutate() return chain.filter(exchange);
// // 为请求追加 Same-Token 参数 }
// .header(SaSameUtil.SAME_TOKEN, SaSameUtil.getToken())
// .build(); @Override
// //结束 public int getOrder() {
// // 解析JWT获取jti以jti为key判断redis的黑名单列表是否存在存在则拦截访问 return 0;
// 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;
// }
//}

View File

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