版本
Springboot版本采用的是最新的:文章来源:https://www.toymoban.com/news/detail-594749.html
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.9</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
网关主要采用的是:文章来源地址https://www.toymoban.com/news/detail-594749.html
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
核心SecurityConfig配置
@EnableWebFluxSecurity
public class SecurityConfig {
@Autowired
PermitUrlConfig permitUrlConfig;
@Autowired
DefaultSecurityContext defaultSecurityContext;
@Autowired
DefaultAuthManager defaultAuthManager;
@Autowired
DefaultAccessDecision defaultAccessDecision;
@Autowired
DefaultAccessDenied defaultAccessDenied;
/**
* 访问权限授权
*
* @param http
* @return
*/
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
.csrf().disable()
.securityContextRepository(defaultSecurityContext) //认证启动
.authenticationManager(defaultAuthManager) //认证管理
.authorizeExchange(exchange -> exchange //请求拦截处理
.pathMatchers(permitUrlConfig.permit()).permitAll() //默认放开的地址
.pathMatchers(HttpMethod.OPTIONS).permitAll() //放开的请求方法
.anyExchange().access(defaultAccessDecision) //其他的地址走后续验证
)
.exceptionHandling().accessDeniedHandler(defaultAccessDenied); //访问拒绝后执行的处理
return http.build();
}
}
定义放开配置的URL
@Component
public class PermitUrlConfig {
public static final String CONTEXT_PATH = "/gateway";
/**
* 需要访问的url
*/
private String[] permitUrl = {
"/actuator/**",
"/api-account/account/**"
};
private String[] heartbeatUrl = {
"/heartbeat/**",
};
/**
* 额外放开权限的url
*
* @param urls 自定义的url
* @return 自定义的url和监控中心需要访问的url集合
*/
public String[] permit(String... urls) {
Set<String> set = new HashSet<>();
if (urls.length > 0) {
Collections.addAll(set, addContextPath(urls));
}
//放开权限的地址
Collections.addAll(set, addContextPath(permitUrl));
Collections.addAll(set, heartbeatUrl);
return set.toArray(new String[set.size()]);
}
/**
* 地址加访问前缀
* @param urls
* @return
*/
private String[] addContextPath(String[] urls) {
for (int i = 0; i < urls.length; i++) {
urls[i] = CONTEXT_PATH + urls[i];
}
return urls;
}
}
核心配置中的处理器
DefaultSecurityContext启动认证
@Component
public class DefaultSecurityContext implements ServerSecurityContextRepository {
@Autowired
DefaultAuthManager defaultAuthManager;
@Override
public Mono<Void> save(ServerWebExchange exchange, SecurityContext context) {
return Mono.empty();
}
@Override
public Mono<SecurityContext> load(ServerWebExchange exchange) {
ServerHttpRequest request = exchange.getRequest();
String authorization = request.getHeaders().getFirst("Authorization");
//调用defaultAuthManager的方法
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(authorization, null);
Mono<Authentication> authenticationMono = defaultAuthManager.authenticate(authenticationToken);
return authenticationMono.map(SecurityContextImpl::new);
}
}
DefaultAuthManager执行认证核心方法
@Slf4j
@Component
public class DefaultAuthManager implements ReactiveAuthenticationManager {
@Override
public Mono<Authentication> authenticate(Authentication authentication) {
String tokenString = (String) authentication.getPrincipal();
String jwtToken = getJwtToken(tokenString);
Map<String, Object> authResult = parseToken(jwtToken);
return Mono.just(authentication).map(auth -> new UsernamePasswordAuthenticationToken(authResult, null, null));
}
/**
* 读取Jwt Token
*
* @param authorization
* @return
*/
private String getJwtToken(String authorization) {
if (!StringUtils.hasText(authorization)) {
return null;
}
boolean valid = authorization.startsWith("Bearer ");
if (!valid) {
return null;
}
return authorization.replace("Bearer ", "");
}
/**
* 校验token
*
* @param jwtToken
* @return
*/
private Map<String, Object> parseToken(String jwtToken) {
log.info("[gateway] jwtToken = {}", jwtToken);
//认证成功
boolean verify = JwtUtils.verify(jwtToken);
if (verify) {
Map<String, Claim> claimMap = JwtUtils.decode(jwtToken);
Claim claim = claimMap.getOrDefault("data", null);
if (Objects.nonNull(claim)) {
return claim.asMap();
}
}
return null;
}
}
DefaultAccessDecision判断认证后是否放行
@Component
public class DefaultAccessDecision implements ReactiveAuthorizationManager<AuthorizationContext> {
@Override
public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext object) {
Mono<AuthorizationDecision> mono = authentication.map(auth -> {
Object authResult = auth.getPrincipal();
//数据读取非空,说明前期在auth的时候,jwt认证返回非空
if (Objects.nonNull(authResult)) {
return new AuthorizationDecision(true);
}
return new AuthorizationDecision(false);
});
return mono;
}
}
DefaultAccessDenied处理不放行的返回
@Slf4j
@Component
public class DefaultAccessDenied implements ServerAccessDeniedHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException denied) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.FORBIDDEN);
response.getHeaders().add("Content-Type", "application/json; charset=UTF-8");
HashMap<String, Object> map = new HashMap<>();
map.put("code", 400);
map.put("message", "未授权禁止访问");
map.put("data", "请检查Token是否合法");
log.error("[DefaultAccessDenied] access forbidden path={}", exchange.getRequest().getPath());
DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONBytes(map));
return response.writeWith(Mono.just(dataBuffer));
}
}
到了这里,关于SpringCloud Gateway + Security + JWT 最快速的集成的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!