栓汨渎 发表于 2025-6-8 12:08:40

SpringSecurity5(14-Gateway整合)

MVC 与 WebFlux 关系

SpringSecurity 设置要采用响应式配置,基于 WebFlux 中 WebFilter 实现,与 Spring MVC 的 Security 是通过 Servlet 的 Filter 实现类似,也是一系列 filter 组成的过滤链。
Reactor 与传统 MVC 配置对应:
webfluxmvc作用@EnableWebFluxSecurity@EnableWebSecurity开启 security 配置ServerAuthenticationSuccessHandlerAuthenticationSuccessHandler登录成功 HandlerServerAuthenticationFailureHandlerAuthenticationFailureHandler登录失败 HandlerServerLogoutSuccessHandlerLogoutSuccessHandler注销成功HandlerServerSecurityContextRepositorySecurityContextHolder认证信息存储管理ReactiveUserDetailsServiceUserDetailsService用户登录逻辑处理ReactiveAuthenticationManagerAuthorizationManager认证管理ReactiveAuthorizationManagerAccessDecisionManager鉴权管理ServerAuthenticationEntryPointAuthenticationEntryPoint未认证 HandlerServerAccessDeniedHandlerAccessDeniedHandler鉴权失败 HandlerAuthenticationWebFilterFilterSecurityInterceptor拦截器快速入门

<dependency>
    <groupId>org.springframework.cloud</groupId>
    spring-cloud-starter-security</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    spring-cloud-starter-gateway</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    fastjson</artifactId>
    <version>2.0.38</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    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 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@Componentpublic class AuthorizeConfigManager implements ReactiveAuthorizationManager {    private final AntPathMatcher antPathMatcher = new AntPathMatcher();    @Override    public Mono check(Mono authentication,                                             AuthorizationContext authorizationContext) {      return authentication.map(auth -> {            ServerWebExchange exchange = authorizationContext.getExchange();            ServerHttpRequest request = exchange.getRequest();            Collection
页: [1]
查看完整版本: SpringSecurity5(14-Gateway整合)