docs:授权服务配置注释完善

This commit is contained in:
haoxr 2020-09-20 23:52:11 +08:00
parent e1d52acda7
commit a1e53c3aba
12 changed files with 51 additions and 118 deletions

View File

@ -58,7 +58,6 @@
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> </dependency>
<!-- nacos 依赖-->
<dependency> <dependency>
<groupId>com.alibaba.cloud</groupId> <groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
@ -68,22 +67,15 @@
<groupId>com.alibaba.cloud</groupId> <groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId> <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.nimbusds</groupId> <groupId>org.springframework.security</groupId>
<artifactId>nimbus-jose-jwt</artifactId> <artifactId>spring-security-oauth2-jose</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -16,13 +16,17 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override @Override
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() http
.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll() .authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
.and().authorizeRequests().antMatchers("/rsa/publicKey").permitAll() .and()
.antMatchers("/v2/api-docs").permitAll() .authorizeRequests().antMatchers("/rsa/publicKey").permitAll().anyRequest().authenticated()
.anyRequest().permitAll(); .and()
.csrf().disable();
} }
/**
* 如果不配置SpringBoot会自动配置一个AuthenticationManager,覆盖掉内存中的用户
*/
@Bean @Bean
public AuthenticationManager authenticationManagerBean() throws Exception { public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean(); return super.authenticationManagerBean();

View File

@ -55,8 +55,6 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId> <artifactId>spring-boot-configuration-processor</artifactId>
</dependency> </dependency>
<!-- 认证依赖 -->
<dependency> <dependency>
<groupId>org.springframework.security</groupId> <groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId> <artifactId>spring-security-config</artifactId>
@ -69,10 +67,7 @@
<groupId>org.springframework.security</groupId> <groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId> <artifactId>spring-security-oauth2-jose</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -1,12 +1,11 @@
package com.youlai.gateway.auth; package com.youlai.gateway.component;
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 cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.nimbusds.jose.JWSObject; import com.nimbusds.jose.JWSObject;
import com.youlai.admin.api.dto.UserDTO; import com.youlai.admin.api.dto.UserDTO;
import com.youlai.common.core.constant.AuthConstants; import com.youlai.common.core.constant.AuthConstants;
import com.youlai.gateway.config.WhiteUrlsConfig; import com.youlai.gateway.config.WhiteListConfig;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
@ -29,7 +28,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* 鉴权管理器用于判断是否有资源的访问权限 * 鉴权管理器
*/ */
@Component @Component
public class AuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> { public class AuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {
@ -38,21 +37,21 @@ public class AuthorizationManager implements ReactiveAuthorizationManager<Author
private RedisTemplate redisTemplate; private RedisTemplate redisTemplate;
@Autowired @Autowired
private WhiteUrlsConfig whiteUrlsConfig; private WhiteListConfig whiteListConfig;
@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();
URI uri = request.getURI(); URI uri = request.getURI();
//白名单路径直接放行 // 白名单路径直接放行
PathMatcher pathMatcher = new AntPathMatcher(); PathMatcher pathMatcher = new AntPathMatcher();
List<String> whiteUrls = whiteUrlsConfig.getUrls(); List<String> whiteUrls = whiteListConfig.getUrls();
for (String ignoreUrl : whiteUrls) { for (String ignoreUrl : whiteUrls) {
if (pathMatcher.match(ignoreUrl, uri.getPath())) { if (pathMatcher.match(ignoreUrl, uri.getPath())) {
return Mono.just(new AuthorizationDecision(true)); return Mono.just(new AuthorizationDecision(true));
} }
} }
//对应跨域的预检请求直接放行 // 对应跨域的预检请求直接放行
if (request.getMethod() == HttpMethod.OPTIONS) { if (request.getMethod() == HttpMethod.OPTIONS) {
return Mono.just(new AuthorizationDecision(true)); return Mono.just(new AuthorizationDecision(true));
} }
@ -97,7 +96,7 @@ public class AuthorizationManager implements ReactiveAuthorizationManager<Author
.filter(Authentication::isAuthenticated) .filter(Authentication::isAuthenticated)
.flatMapIterable(Authentication::getAuthorities) .flatMapIterable(Authentication::getAuthorities)
.map(GrantedAuthority::getAuthority) .map(GrantedAuthority::getAuthority)
.any(roleId -> authorities.contains(roleId)) // 判断用户角色中是否有访问资源权限的角色一个就好 .any(roleId -> authorities.contains(roleId))
.map(AuthorizationDecision::new) .map(AuthorizationDecision::new)
.defaultIfEmpty(new AuthorizationDecision(false)); .defaultIfEmpty(new AuthorizationDecision(false));

View File

@ -17,10 +17,10 @@ import reactor.core.publisher.Mono;
import java.nio.charset.Charset; import java.nio.charset.Charset;
/** /**
* 无权限访问自定义异常 * 无权访问自定义响应
*/ */
@Component @Component
public class AccessDeniedHandler implements ServerAccessDeniedHandler { public class CustomServerAccessDeniedHandler implements ServerAccessDeniedHandler {
@Override @Override
public Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException e) { public Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException e) {

View File

@ -20,7 +20,7 @@ import java.nio.charset.Charset;
* 无效token自定义异常 * 无效token自定义异常
*/ */
@Component @Component
public class AuthExceptionEntryPoint implements ServerAuthenticationEntryPoint { public class CustomServerAuthenticationEntryPoint implements ServerAuthenticationEntryPoint {
@Override @Override
public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException e) { public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException e) {
ServerHttpResponse response=exchange.getResponse(); ServerHttpResponse response=exchange.getResponse();

View File

@ -22,7 +22,7 @@ public class CorsConfig {
config.setAllowCredentials(true); config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser()); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config); source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source); return new CorsWebFilter(source);
} }
} }

View File

@ -3,10 +3,10 @@ package com.youlai.gateway.config;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ArrayUtil;
import com.youlai.common.core.constant.AuthConstants; import com.youlai.common.core.constant.AuthConstants;
import com.youlai.gateway.auth.AuthorizationManager; import com.youlai.gateway.component.AuthorizationManager;
import com.youlai.gateway.component.AuthExceptionEntryPoint; import com.youlai.gateway.component.CustomServerAuthenticationEntryPoint;
import com.youlai.gateway.component.AccessDeniedHandler; import com.youlai.gateway.component.CustomServerAccessDeniedHandler;
import com.youlai.gateway.filter.WhiteUrlsRemoveJwtFilter; import com.youlai.gateway.filter.WhiteListRemoveJwtFilter;
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;
@ -31,37 +31,36 @@ import reactor.core.publisher.Mono;
public class ResourceServerConfig { public class ResourceServerConfig {
private AuthorizationManager authorizationManager; private AuthorizationManager authorizationManager;
private AccessDeniedHandler accessDeniedHandler; private CustomServerAccessDeniedHandler customServerAccessDeniedHandler;
private AuthExceptionEntryPoint authExceptionEntryPoint; private CustomServerAuthenticationEntryPoint customServerAuthenticationEntryPoint;
private WhiteUrlsConfig whiteUrlsConfig; private WhiteListConfig whiteListConfig;
private WhiteUrlsRemoveJwtFilter whiteUrlsRemoveJwtFilter; private WhiteListRemoveJwtFilter whiteListRemoveJwtFilter;
@Bean @Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http.oauth2ResourceServer().jwt() http.oauth2ResourceServer().jwt()
.jwtAuthenticationConverter(jwtAuthenticationConverter()); .jwtAuthenticationConverter(jwtAuthenticationConverter());
// 自定义处理JWT请求头过期或签名错误的结果 // 自定义处理JWT请求头过期或签名错误的结果
http.oauth2ResourceServer().authenticationEntryPoint(authExceptionEntryPoint); http.oauth2ResourceServer().authenticationEntryPoint(customServerAuthenticationEntryPoint);
// 对白名单路径直接移除JWT请求头 // 对白名单路径直接移除JWT请求头
http.addFilterBefore(whiteUrlsRemoveJwtFilter, SecurityWebFiltersOrder.AUTHENTICATION); http.addFilterBefore(whiteListRemoveJwtFilter, SecurityWebFiltersOrder.AUTHENTICATION);
http.authorizeExchange() http.authorizeExchange()
.pathMatchers(ArrayUtil.toArray(whiteUrlsConfig.getUrls(),String.class)).permitAll() .pathMatchers(ArrayUtil.toArray(whiteListConfig.getUrls(),String.class)).permitAll()
.anyExchange().access(authorizationManager) .anyExchange().access(authorizationManager)
.and() .and()
.exceptionHandling() .exceptionHandling()
.accessDeniedHandler(accessDeniedHandler) // 处理未授权 .accessDeniedHandler(customServerAccessDeniedHandler) // 处理未授权
.authenticationEntryPoint(authExceptionEntryPoint) //处理未认证 .authenticationEntryPoint(customServerAuthenticationEntryPoint) //处理未认证
.and().csrf().disable(); .and().csrf().disable();
return http.build(); return http.build();
} }
/** /**
* https://blog.csdn.net/qq_24230139/article/details/105091273 * @linkhttps://blog.csdn.net/qq_24230139/article/details/105091273
* ServerHttpSecurity没有将jwt中authorities的负载部分当做Authentication * ServerHttpSecurity没有将jwt中authorities的负载部分当做Authentication
* 需要把jwt的Claim中的authorities加入 * 需要把jwt的Claim中的authorities加入
* 方案重新定义ReactiveAuthenticationManager权限管理器默认转换器JwtGrantedAuthoritiesConverter * 方案重新定义ReactiveAuthenticationManager权限管理器默认转换器JwtGrantedAuthoritiesConverter
*
* @return * @return
*/ */
@Bean @Bean

View File

@ -1,15 +1,18 @@
package com.youlai.gateway.config; package com.youlai.gateway.config;
import lombok.Data; import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import java.util.List; import java.util.List;
/**
* 白名单配置
*/
@Data @Data
@Configuration @Configuration
@ConfigurationProperties(prefix = "white") @ConfigurationProperties(prefix = "white-list")
public class WhiteUrlsConfig { public class WhiteListConfig {
private List<String> urls; private List<String> urls;
} }

View File

@ -14,15 +14,15 @@ import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* 将登录用户的JWT转换成用户信息过滤器 * JWT转换成用户信息过滤器
*/ */
@Component @Component
@Slf4j @Slf4j
public class AuthGlobalFilter implements GlobalFilter, Ordered { public class AuthGlobalFilter implements GlobalFilter, Ordered {
@SneakyThrows @SneakyThrows
@Override @Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getHeaders().getFirst(AuthConstants.JWT_TOKEN_HEADER); String token = exchange.getRequest().getHeaders().getFirst(AuthConstants.JWT_TOKEN_HEADER);
if (StrUtil.isBlank(token)) { if (StrUtil.isBlank(token)) {
return chain.filter(exchange); return chain.filter(exchange);

View File

@ -1,7 +1,7 @@
package com.youlai.gateway.filter; package com.youlai.gateway.filter;
import com.youlai.common.core.constant.AuthConstants; import com.youlai.common.core.constant.AuthConstants;
import com.youlai.gateway.config.WhiteUrlsConfig; import com.youlai.gateway.config.WhiteListConfig;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -19,10 +19,10 @@ import java.util.List;
* 白名单路径移除JWT请求头 * 白名单路径移除JWT请求头
*/ */
@Component @Component
public class WhiteUrlsRemoveJwtFilter implements WebFilter { public class WhiteListRemoveJwtFilter implements WebFilter {
@Autowired @Autowired
private WhiteUrlsConfig whiteUrlsConfig; private WhiteListConfig whiteListConfig;
@Override @Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
@ -31,7 +31,7 @@ public class WhiteUrlsRemoveJwtFilter implements WebFilter {
PathMatcher pathMatcher=new AntPathMatcher(); PathMatcher pathMatcher=new AntPathMatcher();
//白名单路径移除JWT请求头 //白名单路径移除JWT请求头
List<String> ignoreUrls = whiteUrlsConfig.getUrls(); List<String> ignoreUrls = whiteListConfig.getUrls();
for (String ignoreUrl : ignoreUrls) { for (String ignoreUrl : ignoreUrls) {
if (pathMatcher.match(ignoreUrl, uri.getPath())) { if (pathMatcher.match(ignoreUrl, uri.getPath())) {
request = exchange.getRequest().mutate().header(AuthConstants.JWT_TOKEN_HEADER, "").build(); request = exchange.getRequest().mutate().header(AuthConstants.JWT_TOKEN_HEADER, "").build();

View File

@ -1,59 +0,0 @@
package com.youlai.gateway.handler;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.youlai.common.core.result.Result;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.handler.ResponseStatusExceptionHandler;
import reactor.core.publisher.Mono;
/**
* 网关异常通用处理器只作用在webflux 环境下 , 优先级低于 {@link ResponseStatusExceptionHandler} 执行
*
* @author 冷酱
* @date 2020/5/26
*/
@Slf4j
@Order(-1)
@Configuration
@RequiredArgsConstructor
public class GlobalExceptionHandler 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);
}
// header set
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
if (ex instanceof ResponseStatusException) {
response.setStatusCode(((ResponseStatusException) ex).getStatus());
}
return response.writeWith(Mono.fromSupplier(() -> {
DataBufferFactory bufferFactory = response.bufferFactory();
try {
return bufferFactory.wrap(objectMapper.writeValueAsBytes(Result.error(ex.getMessage())));
}
catch (JsonProcessingException e) {
log.error("Error writing response", ex);
return bufferFactory.wrap(new byte[0]);
}
}));
}
}