SpringSecurity5(14-Gateway整合)
MVC 与 WebFlux 关系
SpringSecurity 设置要采用响应式配置,基于 WebFlux 中 WebFilter 实现,与 Spring MVC 的 Security 是通过 Servlet 的 Filter 实现类似,也是一系列 filter 组成的过滤链。
Reactor 与传统 MVC 配置对应:
| webflux | mvc | 作用 | 
|---|---|---|
| @EnableWebFluxSecurity | @EnableWebSecurity | 开启 security 配置 | 
| ServerAuthenticationSuccessHandler | AuthenticationSuccessHandler | 登录成功 Handler | 
| ServerAuthenticationFailureHandler | AuthenticationFailureHandler | 登录失败 Handler | 
| ServerLogoutSuccessHandler | LogoutSuccessHandler | 注销成功Handler | 
| ServerSecurityContextRepository | SecurityContextHolder | 认证信息存储管理 | 
| ReactiveUserDetailsService | UserDetailsService | 用户登录逻辑处理 | 
| ReactiveAuthenticationManager | AuthorizationManager | 认证管理 | 
| ReactiveAuthorizationManager | AccessDecisionManager | 鉴权管理 | 
| ServerAuthenticationEntryPoint | AuthenticationEntryPoint | 未认证 Handler | 
| ServerAccessDeniedHandler | AccessDeniedHandler | 鉴权失败 Handler | 
| AuthenticationWebFilter | FilterSecurityInterceptor | 拦截器 | 
快速入门
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-security</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>2.0.38</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
内存管理用户信息
@EnableWebFluxSecurity
@Configuration
public class SecurityConfig {
    @Bean
    public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
       http.httpBasic()
               .and()
               .authorizeExchange()
               .anyExchange()
               .authenticated();
        return http.build();
    }
    /**
     * 内存管理用户信息
     */
    @Bean
    public MapReactiveUserDetailsService userDetailsService() {
        UserDetails user = User.withDefaultPasswordEncoder()
                .username("user")
                .password("password")
                .roles("USER")
                .build();
        return new MapReactiveUserDetailsService(user);
    }
}
自定义登录、注销处理器
- 自定义登录成功处理器
 
@Component
public class LoginSuccessHandler implements ServerAuthenticationSuccessHandler {
    @Override
    public Mono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange, Authentication authentication) {
        return Mono.defer(() -> Mono.just(webFilterExchange.getExchange().getResponse()).flatMap(response -> {
            DataBufferFactory dataBufferFactory = response.bufferFactory();
            DataBuffer dataBuffer = dataBufferFactory.wrap("登录成功".getBytes());
            return response.writeWith(Mono.just(dataBuffer));
        }));
    }
}
- 自定义登录失败处理器
 
@Component
public class LoginFailHandler implements ServerAuthenticationFailureHandler {
    @Override
    public Mono<Void> onAuthenticationFailure(WebFilterExchange webFilterExchange, AuthenticationException exception) {
        return Mono.defer(() -> Mono.just(webFilterExchange.getExchange().getResponse()).flatMap(response -> {
            DataBufferFactory dataBufferFactory = response.bufferFactory();
            DataBuffer dataBuffer = dataBufferFactory.wrap("登录失败".getBytes());
            return response.writeWith(Mono.just(dataBuffer));
        }));
    }
}
- 自定义注销成功处理器
 
@Component
public class LogoutSuccessHandler implements ServerLogoutSuccessHandler {
    @Override
    public Mono<Void> onLogoutSuccess(WebFilterExchange exchange, Authentication authentication) {
        return Mono.defer(() -> Mono.just(exchange.getExchange().getResponse()).flatMap(response -> {
            DataBufferFactory dataBufferFactory = response.bufferFactory();
            DataBuffer dataBuffer = dataBufferFactory.wrap("logout success".getBytes());
            return response.writeWith(Mono.just(dataBuffer));
        }));
    }
}
@EnableWebFluxSecurity
@Configuration
public class SecurityConfig {
    @Resource
    private LoginSuccessHandler loginSuccessHandler;
    @Resource
    private LoginFailHandler loginFailHandler;
    @Resource
    private LogoutSuccessHandler logoutSuccessHandler;
    @Bean
    public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
        http.httpBasic()
                .and()
                .authorizeExchange()
                .anyExchange()
                .authenticated();
        http.formLogin()
            .authenticationSuccessHandler(loginSuccessHandler)
            .authenticationFailureHandler(loginFailHandler)
            .and()
            .logout()
            .logoutSuccessHandler(logoutSuccessHandler);
        return http.build();
    }
    /**
     * 内存管理用户信息
     */
    @Bean
    public MapReactiveUserDetailsService userDetailsService() {
        UserDetails user = User.withDefaultPasswordEncoder()
                .username("user")
                .password("password")
                .roles("USER")
                .build();
        return new MapReactiveUserDetailsService(user);
    }
}
自定义用户信息
- 仿照 MapReactiveUserDetailsService 编写获取用户认证类
 
@Component
public class UserDetailService implements ReactiveUserDetailsService, ReactiveUserDetailsPasswordService {
    private final Map<String, UserDetails> users = new HashMap<>();
    @Resource
    private PasswordEncoder passwordEncoder;
    @Override
    public Mono<UserDetails> findByUsername(String username) {
        User user = null;
        if ("user".equals(username)) {
            user = new User("user", passwordEncoder.encode("123456"), true, true, true, true, new ArrayList<>());
        }
        return Mono.justOrEmpty(user);
    }
    @Override
    public Mono<UserDetails> updatePassword(UserDetails user, String newPassword) {
        return Mono.just(user)
                .map(u ->
                        User.withUserDetails(u)
                                .password(newPassword)
                                .build()
                )
                .doOnNext(u -> {
                    this.users.put(user.getUsername().toLowerCase(), u);
                });
    }
}
- 仿照 AbstractUserDetailsReactiveAuthenticationManager 编写用户认证管理类
 
@Component
public class UserAuthenticationManager extends AbstractUserDetailsReactiveAuthenticationManager {
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private ReactiveUserDetailsService userDetailService;
    @Resource
    private ReactiveUserDetailsPasswordService userDetailsPswService;
    private Scheduler scheduler = Schedulers.boundedElastic();
    private UserDetailsChecker preAuthenticationChecks = user -> {
        if (!user.isAccountNonLocked()) {
            logger.debug("User account is locked");
            throw new LockedException(this.messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.locked",
                    "User account is locked"));
        }
        if (!user.isEnabled()) {
            logger.debug("User account is disabled");
            throw new DisabledException(this.messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.disabled",
                    "User is disabled"));
        }
        if (!user.isAccountNonExpired()) {
            logger.debug("User account is expired");
            throw new AccountExpiredException(this.messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.expired",
                    "User account has expired"));
        }
    };
    private UserDetailsChecker postAuthenticationChecks = user -> {
        if (!user.isCredentialsNonExpired()) {
            logger.debug("User account credentials have expired");
            throw new CredentialsExpiredException(this.messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.credentialsExpired",
                    "User credentials have expired"));
        }
    };
    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        final String username = authentication.getName();
        final String presentedPassword = (String) authentication.getCredentials();
        return retrieveUser(username)
                .doOnNext(this.preAuthenticationChecks::check)
                .publishOn(this.scheduler)
                .filter(u -> this.passwordEncoder.matches(presentedPassword, u.getPassword()))
                .switchIfEmpty(Mono.defer(() -> Mono.error(new BadCredentialsException("Invalid Credentials"))))
                .flatMap(u -> {
                    boolean upgradeEncoding = this.userDetailsPswService != null
                            && this.passwordEncoder.upgradeEncoding(u.getPassword());
                    if (upgradeEncoding) {
                        String newPassword = this.passwordEncoder.encode(presentedPassword);
                        return this.userDetailsPswService.updatePassword(u, newPassword);
                    }
                    return Mono.just(u);
                })
                .doOnNext(this.postAuthenticationChecks::check)
                .map(u -> new UsernamePasswordAuthenticationToken(u, u.getPassword(), u.getAuthorities()) );
    }
    @Override
    protected Mono<UserDetails> retrieveUser(String username) {
        return userDetailService.findBysername(username);
    }
}
@EnableWebFluxSecurity
@Configuration
public class SecurityConfig {
    @Resource
    private LoginSuccessHandler loginSuccessHandler;
    @Resource
    private LoginFailHandler loginFailHandler;
    @Resource
    private LogoutSuccessHandler logoutSuccessHandler;
    @Resource
    private UserAuthenticationManager userAuthenticationManager;
    @Bean
    public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
        http.httpBasic()
                .and()
                .authorizeExchange()
                .anyExchange()
                .authenticated();
        http.formLogin()
                .authenticationManager(authenticationManager())
                .authenticationSuccessHandler(loginSuccessHandler)
                .authenticationFailureHandler(loginFailHandler)
				.and()
                .logout()
                .logoutSuccessHandler(logoutSuccessHandler);
        return http.build();
    }
    /**
     * 注册用户信息验证管理器,可按需求添加多个按顺序执行
     */
    @Bean
    public ReactiveAuthenticationManager authenticationManager() {
        LinkedList<ReactiveAuthenticationManager> managers = new LinkedList<>();
        managers.add(userAuthenticationManager);
        return new DelegatingReactiveAuthenticationManager(managers);
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
权限注解
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
@Configuration
public class SecurityConfig {
	// ....
}
@RestController
public class TestController {
    /**
     * 无效
     */
    @Secured({"ROLE_ADMIN"})
    @RequestMapping(value = "/test")
    public Mono<String> test() {
        return Mono.just("test");
    }
    /**
     * 有效
     */
    @PreAuthorize("hasRole('ADMIN')")
    @RequestMapping(value = "/test1")
    public Mono<String> test1() {
        return Mono.just("test1");
    }
    @Secured({"ROLE_TEST"})
    @RequestMapping(value = "/test2")
    public Mono<String> test2() {
        return Mono.just("test2");
    }
}
自定义权限处理器
@Component
public class AccessDeniedHandler implements ServerAccessDeniedHandler {
    @Override
    public Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException denied) {
        return Mono.defer(() -> Mono.just(exchange.getResponse()).flatMap(response -> {
            DataBufferFactory dataBufferFactory = response.bufferFactory();
            DataBuffer dataBuffer = dataBufferFactory.wrap("permission denied".getBytes());
            return response.writeWith(Mono.just(dataBuffer));
        }));
    }
}
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
@Configuration
public class SecurityConfig {
    @Resource
    private LoginSuccessHandler loginSuccessHandler;
    @Resource
    private LoginFailHandler loginFailHandler;
    @Resource
    private LogoutSuccessHandler logoutSuccessHandler;
    @Resource
    private UserAuthenticationManager userAuthenticationManager;
    @Resource
    private AccessDeniedHandler accessDeniedHandler;
    @Bean
    public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
        http.httpBasic()
                .and()
                .authorizeExchange()
                .anyExchange()
                .authenticated();
        http.formLogin()
                .authenticationManager(authenticationManager())
                .authenticationSuccessHandler(loginSuccessHandler)
                .authenticationFailureHandler(loginFailHandler)
                .and()
                .logout()
                .logoutSuccessHandler(logoutSuccessHandler)
                .and()
                .exceptionHandling()
                .accessDeniedHandler(accessDeniedHandler);
        return http.build();
    }
    /**
     * 注册用户信息验证管理器,可按需求添加多个按顺序执行
     */
    @Bean
    public ReactiveAuthenticationManager authenticationManager() {
        LinkedList<ReactiveAuthenticationManager> managers = new LinkedList<>();
        managers.add(userAuthenticationManager);
        return new DelegatingReactiveAuthenticationManager(managers);
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
自定义认证处理器
@Component
public class AuthenticationEntryPoint implements ServerAuthenticationEntryPoint {
    @Override
    public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException e) {
        return Mono.defer(() -> Mono.just(exchange.getResponse()).flatMap(response -> {
            DataBufferFactory dataBufferFactory = response.bufferFactory();
            DataBuffer dataBuffer = dataBufferFactory.wrap("Authentication fail".getBytes());
            return response.writeWith(Mono.just(dataBuffer));
        }));
    }
}
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
@Configuration
public class SecurityConfig {
    @Resource
    private LoginSuccessHandler loginSuccessHandler;
    @Resource
    private LoginFailHandler loginFailHandler;
    @Resource
    private LogoutSuccessHandler logoutSuccessHandler;
    @Resource
    private UserAuthenticationManager userAuthenticationManager;
    @Resource
    private AccessDeniedHandler accessDeniedHandler;
    @Resource
    private AuthenticationEntryPoint authenticationEntryPoint;
    @Bean
    public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
        http.httpBasic()
                .and()
                .authorizeExchange()
                .anyExchange()
                .authenticated();
        http.formLogin()
                .authenticationManager(authenticationManager())
                .authenticationSuccessHandler(loginSuccessHandler)
                .authenticationFailureHandler(loginFailHandler)
                .and()
                .logout()
                .logoutSuccessHandler(logoutSuccessHandler)
                .and()
                .exceptionHandling()
                .accessDeniedHandler(accessDeniedHandler)
                .authenticationEntryPoint(authenticationEntryPoint);
        return http.build();
    }
    /**
     * 注册用户信息验证管理器,可按需求添加多个按顺序执行
     */
    @Bean
    public ReactiveAuthenticationManager authenticationManager() {
        LinkedList<ReactiveAuthenticationManager> managers = new LinkedList<>();
        managers.add(userAuthenticationManager);
        return new DelegatingReactiveAuthenticationManager(managers);
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
自定义鉴权处理器
@Slf4j
@Component
public class AuthorizeConfigManager implements ReactiveAuthorizationManager<AuthorizationContext> {
    private final AntPathMatcher antPathMatcher = new AntPathMatcher();
    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication,
                                             AuthorizationContext authorizationContext) {
        return authentication.map(auth -> {
            ServerWebExchange exchange = authorizationContext.getExchange();
            ServerHttpRequest request = exchange.getRequest();
            Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
            for (GrantedAuthority authority : authorities) {
                String authorityAuthority = authority.getAuthority();
                String path = request.getURI().getPath();
                if (antPathMatcher.match(authorityAuthority, path)) {
                    log.info(String.format("用户请求API校验通过,GrantedAuthority:{%s}  Path:{%s} ", authorityAuthority, path));
                    return new AuthorizationDecision(true);
                }
            }
            return new AuthorizationDecision(false);
        }).defaultIfEmpty(new AuthorizationDecision(false));
    }
    @Override
    public Mono<Void> verify(Mono<Authentication> authentication, AuthorizationContext object) {
        return check(authentication, object)
                .filter(AuthorizationDecision::isGranted)
                .switchIfEmpty(Mono.defer(() -> Mono.error(new AccessDeniedException("Access Denied"))))
                .flatMap(d -> Mono.empty());
    }
}
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
@Configuration
public class SecurityConfig {
    @Resource
    private LoginSuccessHandler loginSuccessHandler;
    @Resource
    private LoginFailHandler loginFailHandler;
    @Resource
    private LogoutSuccessHandler logoutSuccessHandler;
    @Resource
    private UserAuthenticationManager userAuthenticationManager;
    @Resource
    private AccessDeniedHandler accessDeniedHandler;
    @Resource
    private AuthenticationEntryPoint authenticationEntryPoint;
    @Resource
    private AuthorizeConfigManager authorizeConfigManager;
    @Bean
    public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
        http.httpBasic()
                .and()
                .authorizeExchange(e -> e
                        .anyExchange()
                        .access(authorizeConfigManager));
        http.formLogin()
                .authenticationManager(authenticationManager())
                .authenticationSuccessHandler(loginSuccessHandler)
                .authenticationFailureHandler(loginFailHandler)
                .and()
                .logout()
                .logoutSuccessHandler(logoutSuccessHandler)
                .and()
                .exceptionHandling()
                .accessDeniedHandler(accessDeniedHandler)
                .authenticationEntryPoint(authenticationEntryPoint);
        return http.build();
    }
    /**
     * 注册用户信息验证管理器,可按需求添加多个按顺序执行
     */
    @Bean
    public ReactiveAuthenticationManager authenticationManager() {
        LinkedList<ReactiveAuthenticationManager> managers = new LinkedList<>();
        managers.add(userAuthenticationManager);
        return new DelegatingReactiveAuthenticationManager(managers);
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

                    
                
                
            
        
浙公网安备 33010602011771号