我最新最全的文章都在 南瓜慢说 www.pkslow.com ,欢送大家来喝茶!

1 简介

在之前的文章《Springboot集成Spring Security实现JWT认证》解说了如何在传统的Web我的项目中整合Spring SecurityJWT,明天咱们解说如何在响应式WebFlux我的项目中整合。二者大体是雷同的,次要区别在于Reactive WebFlux与传统Web的区别。

2 我的项目整合

引入必要的依赖:

<dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-webflux</artifactId></dependency><dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-security</artifactId></dependency><dependency>  <groupId>io.jsonwebtoken</groupId>  <artifactId>jjwt</artifactId>  <version>0.9.1</version></dependency>

2.1 JWT工具类

该工具类次要性能是创立、校验、解析JWT

@Componentpublic class JwtTokenProvider {    private static final String AUTHORITIES_KEY = "roles";    private final JwtProperties jwtProperties;    private String secretKey;    public JwtTokenProvider(JwtProperties jwtProperties) {        this.jwtProperties = jwtProperties;    }    @PostConstruct    public void init() {        secretKey = Base64.getEncoder().encodeToString(jwtProperties.getSecretKey().getBytes());    }    public String createToken(Authentication authentication) {        String username = authentication.getName();        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();        Claims claims = Jwts.claims().setSubject(username);        if (!authorities.isEmpty()) {            claims.put(AUTHORITIES_KEY, authorities.stream().map(GrantedAuthority::getAuthority).collect(joining(",")));        }        Date now = new Date();        Date validity = new Date(now.getTime() + this.jwtProperties.getValidityInMs());        return Jwts.builder()                .setClaims(claims)                .setIssuedAt(now)                .setExpiration(validity)                .signWith(SignatureAlgorithm.HS256, this.secretKey)                .compact();    }    public Authentication getAuthentication(String token) {        Claims claims = Jwts.parser().setSigningKey(this.secretKey).parseClaimsJws(token).getBody();        Object authoritiesClaim = claims.get(AUTHORITIES_KEY);        Collection<? extends GrantedAuthority> authorities = authoritiesClaim == null ? AuthorityUtils.NO_AUTHORITIES                : AuthorityUtils.commaSeparatedStringToAuthorityList(authoritiesClaim.toString());        User principal = new User(claims.getSubject(), "", authorities);        return new UsernamePasswordAuthenticationToken(principal, token, authorities);    }    public boolean validateToken(String token) {        try {            Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);            if (claims.getBody().getExpiration().before(new Date())) {                return false;            }            return true;        } catch (JwtException | IllegalArgumentException e) {            throw new InvalidJwtAuthenticationException("Expired or invalid JWT token");        }    }}

2.2 JWT的过滤器

这个过滤器的次要性能是从申请中获取JWT,而后进行校验,如何胜利则把Authentication放进ReactiveSecurityContext里去。当然,如果没有带相干的申请头,那可能是通过其它形式进行鉴权,则间接放过,让它进入下一个Filter

public class JwtTokenAuthenticationFilter implements WebFilter {    public static final String HEADER_PREFIX = "Bearer ";    private final JwtTokenProvider tokenProvider;    public JwtTokenAuthenticationFilter(JwtTokenProvider tokenProvider) {        this.tokenProvider = tokenProvider;    }    @Override    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {        String token = resolveToken(exchange.getRequest());        if (StringUtils.hasText(token) && this.tokenProvider.validateToken(token)) {            Authentication authentication = this.tokenProvider.getAuthentication(token);            return chain.filter(exchange)                    .subscriberContext(ReactiveSecurityContextHolder.withAuthentication(authentication));        }        return chain.filter(exchange);    }    private String resolveToken(ServerHttpRequest request) {        String bearerToken = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(HEADER_PREFIX)) {            return bearerToken.substring(7);        }        return null;    }}

2.3 Security的配置

这里设置了两个异样解决authenticationEntryPointaccessDeniedHandler

@Configurationpublic class SecurityConfig {    @Bean    SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http,                                                JwtTokenProvider tokenProvider,                                                ReactiveAuthenticationManager reactiveAuthenticationManager) {        return http.csrf(ServerHttpSecurity.CsrfSpec::disable)                .httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)                .authenticationManager(reactiveAuthenticationManager)                .exceptionHandling().authenticationEntryPoint(                        (swe, e) -> {            swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);            return swe.getResponse().writeWith(Mono.just(new DefaultDataBufferFactory().wrap("UNAUTHORIZED".getBytes())));        })                .accessDeniedHandler((swe, e) -> {            swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN);            return swe.getResponse().writeWith(Mono.just(new DefaultDataBufferFactory().wrap("FORBIDDEN".getBytes())));        }).and()                .securityContextRepository(NoOpServerSecurityContextRepository.getInstance())                .authorizeExchange(it -> it                        .pathMatchers(HttpMethod.POST, "/auth/login").permitAll()                        .pathMatchers(HttpMethod.GET, "/admin").hasRole("ADMIN")                        .pathMatchers(HttpMethod.GET, "/user").hasRole("USER")                        .anyExchange().permitAll()                )                .addFilterAt(new JwtTokenAuthenticationFilter(tokenProvider), SecurityWebFiltersOrder.HTTP_BASIC)                .build();    }    @Bean    public ReactiveAuthenticationManager reactiveAuthenticationManager(CustomUserDetailsService userDetailsService,                                                                       PasswordEncoder passwordEncoder) {        UserDetailsRepositoryReactiveAuthenticationManager authenticationManager = new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService);        authenticationManager.setPasswordEncoder(passwordEncoder);        return authenticationManager;    }}

2.4 获取JWT的Controller

先判断对用户明码进行判断,如果正确则返回对应的权限用户,依据用户生成JWT,再返回给客户端。

@RestController@RequestMapping("/auth")public class AuthController {    @Autowired    ReactiveAuthenticationManager authenticationManager;    @Autowired    JwtTokenProvider jwtTokenProvider;    @PostMapping("/login")    public Mono<String> login(@RequestBody AuthRequest request) {        String username = request.getUsername();        Mono<Authentication> authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, request.getPassword()));        return authentication.map(auth -> jwtTokenProvider.createToken(auth));    }}

3 总结

其它与之前的大同小异,不一一解说了。

代码请查看:https://github.com/LarryDpk/p...


欢送关注微信公众号<南瓜慢说>,将继续为你更新...

多读书,多分享;多写作,多整顿。