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);
}
}

自定义登录、注销处理器

  1. 自定义登录成功处理器
@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));
}));
}
}
  1. 自定义登录失败处理器
@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));
}));
}
}
  1. 自定义注销成功处理器
@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);
}
}

自定义用户信息

  1. 仿照 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);
});
}
}
  1. 仿照 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();
}
}

SpringSecurity5(14-Gateway整合)的更多相关文章

  1. Spring Cloud Gateway 整合阿里 Sentinel网关限流实战!

    大家好,我是不才陈某~ 这是<Spring Cloud 进阶>第八篇文章,往期文章如下: 五十五张图告诉你微服务的灵魂摆渡者Nacos究竟有多强? openFeign夺命连环9问,这谁受得 ...

  2. Springcloud gateway整合(集成)swagger2+finfe4j踩坑

    项目使用gateway代替之前的zuul网关,需要整合swagger,踩了许多坑之后终于解决问题,话不多说直接上代码 因为使用的是阿里的东西所以注册中心选择了nacos,它的配置这里就不贴了 spri ...

  3. Spring Cloud Alibaba(14)---SpringCloudAlibaba整合Sleuth

    SpringCloudAlibaba整合Sleuth 上一篇有写过Sleuth概述,Spring Cloud Alibaba(13)---Sleuth概述 这篇我们开始通过示例来演示链路追踪. 一.环 ...

  4. spring 5.x 系列第14篇 —— 整合RabbitMQ (代码配置方式)

    源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 一.说明 1.1 项目结构说明 本用例关于rabbitmq的整合提供简单消 ...

  5. Spring Cloud Gateway(二):Spring Cloud Gateway整合Eureka应用

    Spring Cloud Gateway 应用概述 下面的示例启动两个服务:gataway-server 和 user-service 都注册到注册中心 Eureka上,客户端请求后端服务[user- ...

  6. Spring Cloud Gateway整合Eureka

    Spring Cloud Gateway features: Built on Spring Framework 5, Project Reactor and Spring Boot 2.0 Able ...

  7. gateway 整合 websocket demo

    背景: 这个websocket  因为使用的地方不多,并没有独立出一个项目,是集成在已有的服务中. 1: gateway 配置 - id: service-test   uri: lb:ws://se ...

  8. springcloud gateway整合sentinel

    1.引入依赖 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spri ...

  9. spring cloud gateway整合sentinel作网关限流

    说明: sentinel可以作为各微服务的限流,也可以作为gateway网关的限流组件. spring cloud gateway有限流功能,但此处用sentinel来作为替待. 说明:sentine ...

  10. 物联网架构成长之路(14)-SpringBoot整合thymeleaf

    使用thymeleaf作为模版进行测试 在pom.xml 增加依赖 <dependency> <groupId>org.springframework.boot</gro ...

随机推荐

  1. biancheng-Spring

    目录http://c.biancheng.net/spring/spring-abc.html 1Spring是什么2Spring体系结构3Spring开发环境搭建4第一个Spring程序5Sprin ...

  2. MySQL的7种JOIN

    原文链接:https://blog.liuzijian.com/post/61e35b3c-fae7-4e0b-aaa2-1d1f2896d9b1.html -- 创建数据库 CREATE DATAB ...

  3. 硬件设计:电路防护--TVS管

    参考资料:TVS参数及选型方法 TVS参数理解 深圳市硕凯电子有限公司元器件产品说明 TVS(Transient Voltage suppressor)或称瞬变电压抑制二极管.当TVS管两端经受瞬间的 ...

  4. 最新demo版|如何0-1开发支付宝小程序之前期准备篇(一)

    小程序作为目前一种轻量.便捷的应用.目前应用越来越广泛了. 很多没有开发经验的开发同学可能初次接触就是小程序开发,为了详细讲解下小程序开发的步骤,我会按照小程序的开发流程一步一步从零开始给大家介绍下如 ...

  5. JAVA基础环境配置指南(简洁版)

    1.安装JDK 官网下载后直接安装 配置环境变量: 添加 JAVA_HOME 变量名:JAVA_HOME 变量值:C:\Program Files (x86)\Java\jdk1.8.0_91 // ...

  6. nginx失效 nginx不起作用

    nginx失效的原因 今天大晚上的,服务器更新了,重启了,然后我重新开一下后端,nginx. 奇了个怪,一直给我报404,而且不是nginx给我报的啊,就是普通的404,完全404了. 我看nginx ...

  7. 【软件开发】Git 概念与常用命令

    [软件开发]Git 概念与常用命令 Git 概念 存储方式 Git 是分布式存储,每一个 clone 下来的仓库都可以看成独立的个体,只是 Git 有提供同步功能,因此 Git 支持离线使用,因为本质 ...

  8. 傻妞PLUS机器人教程——安装

    特性 简单易用的消息搬运功能. 简单强大的自定义回复功能. 完整支持 ECMAScript 5.1 的插件系统,基于 otto. 支持通过内置的阉割版 Express / fetch ,接入互联网. ...

  9. Week09_day05(Java API操作Hbase)

    package com.wyh.HbaseAPI; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbas ...

  10. 咨询公司:趁着AI人工智能的浪潮还能持续,好好享受吧……

    在人工生成式智能热潮的喧嚣与狂热之中,咨询行业正经历一场基本未被察觉却极具变革性的革命,这场变革将塑造它的未来. 传统咨询依赖于由高素质专业人士组成的团队,他们专注于研究.数据分析,并提供定制化建议. ...