This commit is contained in:
parent
e3aa1b0fd0
commit
de5d9ea54c
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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("踢下线");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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", "用户不存在"),
|
||||
|
|
|
@ -28,6 +28,9 @@ public class HspHostUtil {
|
|||
public static void setHspHost(String hspHost) {
|
||||
HSP_HSOT.set(hspHost);
|
||||
}
|
||||
public static void clear() {
|
||||
HSP_HSOT.remove();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
||||
|
|
|
@ -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());
|
||||
// })
|
||||
// ;
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
8
pom.xml
8
pom.xml
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue