This commit is contained in:
ytChen 2024-03-11 14:36:28 +08:00
parent c3c02a9473
commit e3aa1b0fd0
20 changed files with 419 additions and 925 deletions

View File

@ -53,6 +53,21 @@
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency> </dependency>
<!-- Sa-Token 权限认证, 在线文档https://sa-token.cc -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.37.0</version>
<exclusions>
<exclusion>
<!-- 配置冲突,需要排除 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- JWT库 --> <!-- JWT库 -->
<dependency> <dependency>
<groupId>com.nimbusds</groupId> <groupId>com.nimbusds</groupId>

View File

@ -0,0 +1,35 @@
package com.recovery.admin.boot.config;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.same.SaSameUtil;
import cn.dev33.satoken.util.SaResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* Sa-Token 权限认证 配置类
*/
@Configuration
@Slf4j
public class SaTokenConfigure implements WebMvcConfigurer {
// 注册 Sa-Token 全局过滤器
@Bean
public SaServletFilter getSaServletFilter() {
log.info("校验是否是网关转发请求:====================");
return new SaServletFilter()
.addInclude("/**")
.addExclude("/favicon.ico")
.setAuth(obj -> {
// 校验 Same-Token 身份凭证 以下两句代码可简化为SaSameUtil.checkCurrentRequestToken();
String token = SaHolder.getRequest().getHeader(SaSameUtil.SAME_TOKEN);
SaSameUtil.checkToken(token);
})
.setError(e -> {
return SaResult.error(e.getMessage());
})
;
}
}

View File

@ -35,6 +35,15 @@
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency> </dependency>
<!-- Sa-Token 权限认证, 在线文档https://sa-token.cc -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.37.0</version>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId> <artifactId>spring-cloud-starter-loadbalancer</artifactId>
@ -46,25 +55,13 @@
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.recovery</groupId> <groupId>com.recovery</groupId>
<artifactId>common-web</artifactId> <artifactId>common-web</artifactId>
<version>${hoe-version}</version> <version>${hoe-version}</version>
</dependency> </dependency>
<!-- OAuth2 认证服务器-->
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>

View File

@ -1,107 +0,0 @@
package com.recovery.auth.comm.exception;
import com.recovery.common.base.result.ApiResult;
import com.recovery.common.base.result.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.NoSuchClientException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* @author
*/
@RestControllerAdvice
@Slf4j
@Order(-1)
public class AuthExceptionHandler {
/**
* 用户不存在
*
* @param e
* @return
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(UsernameNotFoundException.class)
public ApiResult handleUsernameNotFoundException(UsernameNotFoundException e) {
log.error("错误信息:{}", e.getMessage(),e);
return ApiResult.failed(ResultCode.USER_NOT_EXIST);
}
/**
* 用户名和密码异常
*
* @param e
* @return
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(InvalidGrantException.class)
public ApiResult handleInvalidGrantException(InvalidGrantException e) {
log.error("错误信息:{}", e.getMessage(),e);
return ApiResult.failed(ResultCode.USERNAME_OR_PASSWORD_ERROR);
}
/**
* 用户名和密码异常
*
* @param e
* @return
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(InvalidClientException.class)
public ApiResult handleInvalidGrantException(InvalidClientException e) {
log.error("错误信息:{}", e.getMessage(),e);
return ApiResult.failed(ResultCode.CLIENT_AUTHENTICATION_FAILED);
}
/**
* 账户异常(禁用锁定过期)
*
* @param e
* @return
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler({InternalAuthenticationServiceException.class})
public ApiResult handleInternalAuthenticationServiceException(InternalAuthenticationServiceException e) {
log.error("错误信息:{}", e.getMessage(),e);
return ApiResult.failed(e.getMessage());
}
/**
* token 无效或已过期
*
* @param e
* @return
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler({InvalidTokenException.class})
public ApiResult handleInvalidTokenExceptionException(InvalidTokenException e) {
log.error("错误信息:{}", e.getMessage(),e);
return ApiResult.failed(e.getMessage());
}
/**
* token 无效或已过期
*
* @param e
* @return
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler({NoSuchClientException.class})
public ApiResult noSuchClientException(NoSuchClientException e) {
log.error("错误信息:{}", e.getMessage(),e);
return ApiResult.failed(e.getMessage());
}
}

View File

@ -1,36 +0,0 @@
package com.recovery.auth.comm.utils;
import cn.hutool.core.util.StrUtil;
import com.recovery.common.base.constant.SecurityConstants;
import org.apache.logging.log4j.util.Strings;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
/**
* @author
* @date: 2022/5/23
*/
public class CommonUtils {
public static String getOAuth2ClientId() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String clientId = request.getParameter(SecurityConstants.CLIENT_ID_KEY);
if (StrUtil.isNotBlank(clientId)) {
return clientId;
}
String basic = request.getHeader(SecurityConstants.AUTHORIZATION_KEY);
if (StrUtil.isNotBlank(basic) && basic.startsWith(SecurityConstants.BASIC_PREFIX)) {
basic = basic.replace(SecurityConstants.BASIC_PREFIX, Strings.EMPTY);
String basicPlainText = new String(Base64.getDecoder().decode(basic.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
clientId = basicPlainText.split(":")[0];
}
return clientId;
}
}

View File

@ -1,138 +0,0 @@
package com.recovery.auth.config;
import cn.hutool.core.collection.CollectionUtil;
import com.recovery.auth.security.details.client.ClientDetailsServiceImpl;
import com.recovery.auth.security.details.user.SysUserDetails;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import java.security.KeyPair;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* Created with IntelliJ IDEA.
*
* @author
* @date 2021/11/24
* @description
* @modifiedBy
* @version: 1.0
*/
@Configuration
@EnableAuthorizationServer
@RequiredArgsConstructor
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
private final ClientDetailsServiceImpl clientDetailsService;
/**
* OAuth2客户端
*/
@Override
@SneakyThrows
public void configure(ClientDetailsServiceConfigurer clients) {
clients.withClientDetails(clientDetailsService);
}
/**
* 配置授权authorization以及令牌token的访问端点和令牌服务(token services)
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
// Token增强
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
List<TokenEnhancer> tokenEnhancers = new ArrayList<>();
tokenEnhancers.add(tokenEnhancer());
tokenEnhancers.add(jwtAccessTokenConverter());
tokenEnhancerChain.setTokenEnhancers(tokenEnhancers);
// 获取原有默认授权模式(授权码模式密码模式客户端模式简化模式)的授权者
List<TokenGranter> granterList = new ArrayList<>(Arrays.asList(endpoints.getTokenGranter()));
CompositeTokenGranter compositeTokenGranter = new CompositeTokenGranter(granterList);
endpoints
.authenticationManager(authenticationManager)
.accessTokenConverter(jwtAccessTokenConverter())
.tokenEnhancer(tokenEnhancerChain)
.tokenGranter(compositeTokenGranter)
.reuseRefreshTokens(true)
.tokenServices(tokenServices(endpoints))
;
}
public DefaultTokenServices tokenServices(AuthorizationServerEndpointsConfigurer endpoints) {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
List<TokenEnhancer> tokenEnhancers = new ArrayList<>();
tokenEnhancers.add(tokenEnhancer());
tokenEnhancers.add(jwtAccessTokenConverter());
tokenEnhancerChain.setTokenEnhancers(tokenEnhancers);
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(endpoints.getTokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setClientDetailsService(clientDetailsService);
tokenServices.setTokenEnhancer(tokenEnhancerChain);
return tokenServices;
}
/**
* JWT内容增强
*/
@Bean
public TokenEnhancer tokenEnhancer() {
return (accessToken, authentication) -> {
Map<String, Object> additionalInfo = CollectionUtil.newHashMap();
Object principal = authentication.getUserAuthentication().getPrincipal();
if (principal instanceof SysUserDetails){
SysUserDetails sysUserDetails = (SysUserDetails) principal;
additionalInfo.put("userId", sysUserDetails.getUserId());
additionalInfo.put("username", sysUserDetails.getUsername());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
}
return accessToken;
};
}
/**
* 使用非对称加密算法对token签名
*/
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setKeyPair(keyPair());
return converter;
}
/**
* 密钥库中获取密钥对(公钥+私钥)
*/
@Bean
public KeyPair keyPair() {
KeyStoreKeyFactory factory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "afd123".toCharArray());
KeyPair keyPair = factory.getKeyPair("jwt", "afd123".toCharArray());
return keyPair;
}
}

View File

@ -0,0 +1,32 @@
package com.recovery.auth.config;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.same.SaSameUtil;
import cn.dev33.satoken.util.SaResult;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* Sa-Token 权限认证 配置类
*/
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
// 注册 Sa-Token 全局过滤器
@Bean
public SaServletFilter getSaServletFilter() {
return new SaServletFilter()
.addInclude("/**")
.addExclude("/favicon.ico")
.setAuth(obj -> {
// 校验 Same-Token 身份凭证 以下两句代码可简化为SaSameUtil.checkCurrentRequestToken();
String token = SaHolder.getRequest().getHeader(SaSameUtil.SAME_TOKEN);
SaSameUtil.checkToken(token);
})
.setError(e -> {
return SaResult.error(e.getMessage());
})
;
}
}

View File

@ -1,85 +0,0 @@
package com.recovery.auth.config;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* Created with IntelliJ IDEA.
*
* @author
* @date 2021/11/24
* @description
* @modifiedBy
* @version: 1.0
*/
@Configuration
@EnableWebSecurity
@Slf4j
@RequiredArgsConstructor
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService sysUserDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/api/oauth/**").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().disable();
}
/**
* 认证管理对象
*
* @return
* @throws Exception
*/
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* 添加自定义认证器
*
* @param auth
*/
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
/**
* 设置默认的用户名密码认证授权提供者
*
* @return
*/
@Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(sysUserDetailsService);
provider.setPasswordEncoder(passwordEncoder());
provider.setHideUserNotFoundExceptions(false); // 是否隐藏用户不存在异常默认:true-隐藏false-抛出异常
return provider;
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}

View File

@ -1,25 +1,25 @@
package com.recovery.auth.controller; package com.recovery.auth.controller;
import com.nimbusds.jose.jwk.JWKSet; import cn.dev33.satoken.stp.SaTokenInfo;
import com.nimbusds.jose.jwk.RSAKey; import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import com.recovery.auth.exception.BusinessException;
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.dto.UserAuthDTO;
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 lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.util.StringUtils;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.security.KeyPair;
import java.security.Principal;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
/** /**
* @author * @author
@ -32,6 +32,8 @@ public class AuthController {
@Resource @Resource
AuthService authService; AuthService authService;
@Resource
UserFeignClient userFeignClient;
@PostMapping("/token") @PostMapping("/token")
public ApiResult postAccessToken(@RequestBody JwtAuthenticationRequest authenticationRequest, HttpServletRequest request){ public ApiResult postAccessToken(@RequestBody JwtAuthenticationRequest authenticationRequest, HttpServletRequest request){
@ -46,6 +48,32 @@ public class AuthController {
return ApiResult.ok(map); return ApiResult.ok(map);
} }
@GetMapping("/doLogin")
public SaResult doLogin(@RequestBody JwtAuthenticationRequest authenticationRequest) {
if(StringUtils.isEmpty(authenticationRequest.getUsername())){
throw new BusinessException("账户不能为空");
}
if(StringUtils.isEmpty(authenticationRequest.getPassword())){
throw new BusinessException("密码不能为空");
}
ApiResult<UserAuthDTO> result = userFeignClient.getUserByUsername(authenticationRequest.getUsername());
UserAuthDTO userDetails = new UserAuthDTO();
if (ResultCode.SUCCESS.getCode().equals(result.getCode())) {
userDetails = result.getData();
}
// 此处仅作模拟示例真实项目需要从数据库中查询数据进行比对
if (userDetails.getUserName().equals(authenticationRequest.getUsername()) && userDetails.getPassword().equals(authenticationRequest.getPassword())) {
log.info("密码校验成功!");
StpUtil.login(userDetails.getUserName());
}else {
return SaResult.error("密码错误");
}
// 第3步返回给前端
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
return SaResult.ok("登录成功").setData(tokenInfo);
}
// @GetMapping("/public-key") // @GetMapping("/public-key")
// public Map<String, Object> getPublicKey() { // public Map<String, Object> getPublicKey() {
// //单例模式 // //单例模式

View File

@ -1,43 +0,0 @@
package com.recovery.auth.security.details.client;
import com.recovery.common.base.enums.PasswordEncoderTypeEnum;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.stereotype.Service;
/**
* Created with IntelliJ IDEA.
*
* @author AI码师
* @date 2021/11/24
* @description
* @modifiedBy
* @version: 1.0
*/
@Service
@RequiredArgsConstructor
public class ClientDetailsServiceImpl implements ClientDetailsService {
@Override
@Cacheable(cacheNames = "auth", key = "'oauth-client:'+#clientId")
public ClientDetails loadClientByClientId(String clientId) {
// 后面通过feign从管理端获取目前写死
BaseClientDetails clientDetails = new BaseClientDetails(
"hoe",
"",
"all",
"password,client_credentials,refresh_token,authorization_code",
"",
"http://www.baidu.com"
);
clientDetails.setClientSecret(PasswordEncoderTypeEnum.NOOP.getPrefix() + "hoe");
clientDetails.setAccessTokenValiditySeconds(3600);
clientDetails.setRefreshTokenValiditySeconds(36000000);
return clientDetails;
}
}

View File

@ -1,74 +0,0 @@
package com.recovery.auth.security.details.user;
import java.io.Serializable;
public class JwtAuthenticationRequest implements Serializable {
private static final long serialVersionUID = -8445943548965154778L;
private String username;
private String phone;
private String password;
private String verifyCode;
private String loginMethod;
private String visitorsType;
public JwtAuthenticationRequest(String username,String phone,String password,String verifyCode,String loginMethod,String visitorsType) {
this.username = username;
this.phone = phone;
this.password = password;
this.verifyCode = verifyCode;
this.loginMethod = loginMethod;
this.visitorsType = visitorsType;
}
public JwtAuthenticationRequest() {
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUserPhone() {
return phone;
}
public void setUserPhone(String phone) {
this.phone = phone;
}
public String getVerifyCode() {
return verifyCode;
}
public void setVerifyCode(String verifyCode) {
this.verifyCode = verifyCode;
}
public String getLoginMethod() {
return loginMethod;
}
public void setLoginMethod(String loginMethod) {
this.loginMethod = loginMethod;
}
public String getVisitorsType() {
return visitorsType;
}
public void setVisitorsType(String visitorsType) {
this.visitorsType = visitorsType;
}
}

View File

@ -1,76 +0,0 @@
package com.recovery.auth.security.details.user;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
/**
* @author
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class SysUserDetails implements UserDetails {
/**
* 扩展字段
*/
private Long userId;
/**
* 默认字段
*/
private String username;
private String password;
/**
* 是否启用
*/
private Boolean enabled;
/**
* 角色
*/
private Collection<SimpleGrantedAuthority> authorities;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.authorities;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return this.enabled;
}
}

View File

@ -1,79 +0,0 @@
package com.recovery.auth.security.details.user;
import com.recovery.auth.feign.UserFeignClient;
import com.recovery.common.base.dto.UserAuthDTO;
import com.recovery.common.base.enums.PasswordEncoderTypeEnum;
import com.recovery.common.base.result.ApiResult;
import com.recovery.common.base.result.ResultCode;
import com.recovery.common.base.util.RedisUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
/**
* @author
*/
@Service("sysUserDetailsService")
@Slf4j
@RequiredArgsConstructor
public class SysUserDetailsServiceImpl implements UserDetailsService {
@Resource
private UserFeignClient userFeignClient;
@Resource
RedisUtil redisUtil;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 后面从管理端获取用户信息
ApiResult<UserAuthDTO> result = userFeignClient.getUserByUsername(username);
SysUserDetails userDetails = null;
if (ApiResult.ok().getCode().equals(result.getCode())) {
UserAuthDTO user = result.getData();
if (null != user) {
userDetails = SysUserDetails.builder()
.userId(user.getUserId())
.username(user.getUserName())
//角色
// .authorities(handleRoles(user.getRoles()))
.enabled(user.getStatus() == 1)
.password(PasswordEncoderTypeEnum.BCRYPT.getPrefix() + user.getPassword())
.build();
}
}
if (Objects.isNull(userDetails)) {
throw new UsernameNotFoundException(ResultCode.USER_NOT_EXIST.getMsg());
} else if (!userDetails.isEnabled()) {
throw new DisabledException("该账户已被禁用!");
} else if (!userDetails.isAccountNonLocked()) {
throw new LockedException("该账号已被锁定!");
} else if (!userDetails.isAccountNonExpired()) {
throw new AccountExpiredException("该账号已过期!");
}
return userDetails;
}
private Collection<SimpleGrantedAuthority> handleRoles(List<String> roles) {
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
}

View File

@ -1,11 +1,9 @@
package com.recovery.auth.service.impl; package com.recovery.auth.service.impl;
import com.alibaba.fastjson.JSON;
import com.recovery.auth.exception.BusinessException; 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.security.details.user.SysUserDetails;
import com.recovery.auth.service.AuthService; import com.recovery.auth.service.AuthService;
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;
@ -15,16 +13,11 @@ import com.recovery.common.base.util.EncryptUtil;
import com.recovery.common.base.util.RedisUtil; import com.recovery.common.base.util.RedisUtil;
import com.recovery.common.base.utils.JwtUtils; import com.recovery.common.base.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**

View File

@ -48,6 +48,22 @@
<artifactId>spring-jdbc</artifactId> <artifactId>spring-jdbc</artifactId>
</dependency> </dependency>
<!-- httpClient依赖,缺少此依赖api网关转发请求时可能发生503错误 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-reactor-spring-boot-starter</artifactId>
<version>1.34.0</version>
</dependency>
<!-- MySQL 连接驱动依赖 --> <!-- MySQL 连接驱动依赖 -->
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>

View File

@ -0,0 +1,42 @@
package com.recovery.gateway.config;
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 org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* [Sa-Token 权限认证] 配置类
*/
@Configuration
public class SaTokenConfigure {
// 注册 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,122 +1,79 @@
package com.recovery.gateway.security; //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 AI码师 关注公众号"AI码师"获取完整源码
* @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));
// }
// // 如果token为空 或者token不合法 则进行拦截
// String restfulPath = method + ":" + path; // RESTFul接口权限设计 @link https://www.cnblogs.com/haoxianrui/p/14961707.html
// String token = request.getHeaders().getFirst(SecurityConstants.AUTHORIZATION_KEY);
// if (StrUtil.isBlank(token) || !StrUtil.startWithIgnoreCase(token, SecurityConstants.JWT_PREFIX)) {
// return Mono.just(new AuthorizationDecision(false));
// }
// //
// // 从redis中获取资源权限 //import cn.hutool.core.collection.CollectionUtil;
// Map<String, Object> urlPermRolesRules = redisTemplate.opsForHash().entries(GlobalConstants.URL_PERM_ROLES_KEY); //import cn.hutool.core.convert.Convert;
// List<String> authorizedRoles = new ArrayList<>(); // 拥有访问权限的角色 //import cn.hutool.core.util.StrUtil;
// boolean requireCheck = false; // 是否需要鉴权默认未设置拦截规则不需鉴权
// //
// // 获取当前资源 所需要的角色 //import com.recovery.common.base.constant.GlobalConstants;
// for (Map.Entry<String, Object> permRoles : urlPermRolesRules.entrySet()) { //import com.recovery.common.base.constant.SecurityConstants;
// String perm = permRoles.getKey(); //import com.recovery.gateway.util.UrlPatternUtils;
// if (pathMatcher.match(perm, restfulPath)) { //import lombok.RequiredArgsConstructor;
// List<String> roles = Convert.toList(String.class, permRoles.getValue()); //import lombok.Setter;
// authorizedRoles.addAll(Convert.toList(String.class, roles)); //import lombok.extern.slf4j.Slf4j;
// if (requireCheck == false) { //import org.springframework.boot.context.properties.ConfigurationProperties;
// requireCheck = true; //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;
// if (!requireCheck) { //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)); // return Mono.just(new AuthorizationDecision(true));
// } // }
// PathMatcher pathMatcher = new AntPathMatcher();
// String method = request.getMethodValue();
// String path = request.getURI().getPath();
// //
// // 判断JWT中携带的用户角色是否有权限访问 // // 跳过token校验放在这里去做是为了能够动态刷新
// Mono<AuthorizationDecision> authorizationDecisionMono = mono //// if (skipValid(path)) {
// .filter(Authentication::isAuthenticated) // return Mono.just(new AuthorizationDecision(true));
// .flatMapIterable(Authentication::getAuthorities) //// }
// .map(GrantedAuthority::getAuthority) // }
// .any(authority -> { //
// String roleCode = authority.substring(SecurityConstants.AUTHORITY_PREFIX.length()); // 用户的角色 // /**
// boolean hasAuthorized = CollectionUtil.isNotEmpty(authorizedRoles) && authorizedRoles.contains(roleCode); // * 跳过校验
// return hasAuthorized; // *
// }) // * @param path
// .map(AuthorizationDecision::new) // * @return
// .defaultIfEmpty(new AuthorizationDecision(false)); // */
// return authorizationDecisionMono; // private boolean skipValid(String path) {
} // for (String skipPath : ignoreUrls) {
// if (UrlPatternUtils.match(skipPath, path)) {
/** // return true;
* 跳过校验 // }
* // }
* @param path // return false;
* @return // }
*/ //}
private boolean skipValid(String path) {
for (String skipPath : ignoreUrls) {
if (UrlPatternUtils.match(skipPath, path)) {
return true;
}
}
return false;
}
}

View File

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

10
pom.xml
View File

@ -46,6 +46,16 @@
<artifactId>hutool-all</artifactId> <artifactId>hutool-all</artifactId>
<version>${hutool-version}</version> <version>${hutool-version}</version>
</dependency> </dependency>
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-jackson</artifactId>
<version>1.37.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
</dependencies> </dependencies>
<!-- end --> <!-- end -->
<dependencyManagement> <dependencyManagement>