SpringCloud GateWay 从Redis中读取OAuth2并校验
ResourceServerConfig/*** 资源服务器配置* @author cmy* @date 2021/5/21 15:23*/@AllArgsConstructor@Configuration@EnableWebFluxSecuritypublic class ResourceServerConfig {@AutowiredAuthorizationManager authoriza
·
ResourceServerConfig
/**
* 资源服务器配置
* @author cmy
* @date 2021/5/21 15:23
*/
@AllArgsConstructor
@Configuration
@EnableWebFluxSecurity
public class ResourceServerConfig {
@Autowired
AuthorizationManager authorizationManager;
@Autowired
IgnoreUrlsConfig ignoreUrlsConfig;
@Autowired
RestfulAccessDeniedHandler restfulAccessDeniedHandler;
@Autowired
RestAuthenticationEntryPoint restAuthenticationEntryPoint;
@Autowired
ReactiveRedisAuthenticationManager reactiveRedisAuthenticationManager;
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
//认证过滤器
AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(reactiveRedisAuthenticationManager);
authenticationWebFilter.setServerAuthenticationConverter(new ServerBearerTokenAuthenticationConverter());
http.addFilterAt(authenticationWebFilter,SecurityWebFiltersOrder.AUTHENTICATION);
http.authorizeExchange()
.pathMatchers(ignoreUrlsConfig.getUrls().toArray(new String[ignoreUrlsConfig.getUrls().size()])).permitAll()
.anyExchange().access(authorizationManager)
.and().exceptionHandling()
//处理未授权
.accessDeniedHandler(restfulAccessDeniedHandler)
//处理未认证
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and().csrf().disable();
return http.build();
}
}
鉴权管理
/**
* 鉴权管理器,用于判断是否有资源的访问权限
* @author cmy
* @date 2021/5/21 15:24
*/
@Slf4j
@Component
public class AuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {
@Autowired
private MenuRoleService menuRoleService;
AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
List<MenuRoles> menus = menuRoleService.getAllMenusWithRole();
String urlPath = authorizationContext.getExchange().getRequest().getURI().getPath();
List<String> authorities = new ArrayList<>();
for (MenuRoles bean : menus) {
if (antPathMatcher.match(bean.getUrl(), urlPath)) {
authorities.addAll(bean.getRoleName());
}
}
Mono<AuthorizationDecision> authorizationDecisionMono = mono
.filter(Authentication::isAuthenticated)
.flatMapIterable(Authentication::getAuthorities)
.map(GrantedAuthority::getAuthority)
.any(roleName -> {
log.info("访问路径:{}", urlPath);
log.info("用户角色roleName:{}", roleName);
log.info("资源需要权限authorities:{}", authorities);
return authorities.contains(roleName);
})
.map(AuthorizationDecision::new)
.defaultIfEmpty(new AuthorizationDecision(false));
return authorizationDecisionMono;
}
}
自定义认证
注意:Redis序列化了User对象,这边反序列化需要包同名
/**
* @author cmy
* @date 2021/5/21 15:34
*/
@Slf4j
@Component
public class ReactiveRedisAuthenticationManager implements ReactiveAuthenticationManager {
@Resource
TokenStore redisTokenStore;
@Override
public Mono<Authentication> authenticate(Authentication authentication) {
return Mono.justOrEmpty(authentication)
.filter(a -> a instanceof BearerTokenAuthenticationToken)
.cast(BearerTokenAuthenticationToken.class)
.map(BearerTokenAuthenticationToken::getToken)
.flatMap((accessToken ->{
log.info("accessToken is :{}",accessToken);
OAuth2AccessToken oAuth2AccessToken = this.redisTokenStore.readAccessToken(accessToken);
//根据access_token从数据库获取不到OAuth2AccessToken
if(oAuth2AccessToken == null){
return Mono.error(new InvalidTokenException("invalid access token,please check"));
}else if(oAuth2AccessToken.isExpired()){
return Mono.error(new InvalidTokenException("access token has expired,please reacquire token"));
}
OAuth2Authentication oAuth2Authentication =this.redisTokenStore.readAuthentication(accessToken);
if(oAuth2Authentication == null){
return Mono.error(new InvalidTokenException("Access Token 无效!"));
}else {
return Mono.just(oAuth2Authentication);
}
})).cast(Authentication.class);
}
}
处理抛出异常
@Slf4j
@Order(-1)
@Configuration
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class GlobalErrorWebExceptionHandler implements ErrorWebExceptionHandler {
private final ObjectMapper objectMapper;
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
ServerHttpResponse response = exchange.getResponse();
if (response.isCommitted()) {
return Mono.error(ex);
}
// 设置返回JSON
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
if(ex instanceof InvalidTokenException){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
}
return response.writeWith(Mono.fromSupplier(() -> {
DataBufferFactory bufferFactory = response.bufferFactory();
try {
//返回响应结果
Map<String,Object> result = new HashMap<>(2);
result.put("code",response.getStatusCode().value());
result.put("msg",ex.getMessage());
return bufferFactory.wrap(objectMapper.writeValueAsBytes(result));
}
catch (JsonProcessingException e) {
log.error("Error writing response", ex);
return bufferFactory.wrap(new byte[0]);
}
}));
}
}
TokenStore
@Configuration
public class TokenStoreConfig {
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Bean
public TokenStore redisTokenStore(){
//使用redis存储token
RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
//设置redis token存储中的前缀
redisTokenStore.setPrefix("auth-token:");
return redisTokenStore;
}
}
处理未授权
/**
* 自定义返回结果:没有权限访问时
* @author cmy
* @date 2021/5/21 15:26
*/
@Component
public class RestfulAccessDeniedHandler implements ServerAccessDeniedHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException denied) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.FORBIDDEN);
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
Map<String,Object> map = new HashMap<>(2);
map.put("code",403);
map.put("msg",denied.getMessage());
String body = JSON.toJSONString(map);
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(Charset.forName("UTF-8")));
return response.writeWith(Mono.just(buffer));
}
}
处理未认证
@Component
public class RestAuthenticationEntryPoint implements ServerAuthenticationEntryPoint {
@Override
public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException e) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
System.out.println("RestAuthenticationEntryPoint execute" );
Map<String,Object> map = new HashMap<>(2);
map.put("code",401);
map.put("msg",e.getMessage());
String body = JSON.toJSONString(map);
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(Charset.forName("UTF-8")));
return response.writeWith(Mono.just(buffer));
}
}
更多推荐
已为社区贡献2条内容
所有评论(0)