版本
Springboot版本采用的是最新的:文章來源:http://www.zghlxwxcb.cn/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>
網(wǎng)關(guān)主要采用的是:文章來源地址http://www.zghlxwxcb.cn/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;
/**
* 訪問權(quán)限授權(quán)
*
* @param http
* @return
*/
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
.csrf().disable()
.securityContextRepository(defaultSecurityContext) //認(rèn)證啟動(dòng)
.authenticationManager(defaultAuthManager) //認(rèn)證管理
.authorizeExchange(exchange -> exchange //請(qǐng)求攔截處理
.pathMatchers(permitUrlConfig.permit()).permitAll() //默認(rèn)放開的地址
.pathMatchers(HttpMethod.OPTIONS).permitAll() //放開的請(qǐng)求方法
.anyExchange().access(defaultAccessDecision) //其他的地址走后續(xù)驗(yàn)證
)
.exceptionHandling().accessDeniedHandler(defaultAccessDenied); //訪問拒絕后執(zhí)行的處理
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/**",
};
/**
* 額外放開權(quán)限的url
*
* @param urls 自定義的url
* @return 自定義的url和監(jiān)控中心需要訪問的url集合
*/
public String[] permit(String... urls) {
Set<String> set = new HashSet<>();
if (urls.length > 0) {
Collections.addAll(set, addContextPath(urls));
}
//放開權(quán)限的地址
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啟動(dòng)認(rèn)證
@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");
//調(diào)用defaultAuthManager的方法
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(authorization, null);
Mono<Authentication> authenticationMono = defaultAuthManager.authenticate(authenticationToken);
return authenticationMono.map(SecurityContextImpl::new);
}
}
DefaultAuthManager執(zhí)行認(rèn)證核心方法
@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 ", "");
}
/**
* 校驗(yàn)token
*
* @param jwtToken
* @return
*/
private Map<String, Object> parseToken(String jwtToken) {
log.info("[gateway] jwtToken = {}", jwtToken);
//認(rèn)證成功
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判斷認(rèn)證后是否放行
@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();
//數(shù)據(jù)讀取非空,說明前期在auth的時(shí)候,jwt認(rèn)證返回非空
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", "未授權(quán)禁止訪問");
map.put("data", "請(qǐng)檢查Token是否合法");
log.error("[DefaultAccessDenied] access forbidden path={}", exchange.getRequest().getPath());
DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONBytes(map));
return response.writeWith(Mono.just(dataBuffer));
}
}
到了這里,關(guān)于SpringCloud Gateway + Security + JWT 最快速的集成的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!