一、主要pom依赖包
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-security</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-pool2</artifactId>
- <version>2.5.0</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-webflux</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.session</groupId>
- <artifactId>spring-session-core</artifactId>
- </dependency>
- <!-- 使用redis存储session -->
- <dependency>
- <groupId>org.springframework.session</groupId>
- <artifactId>spring-session-data-redis</artifactId>
- </dependency>
二、application root配置
- @SpringBootApplication
- //开启webflux
- @EnableWebFlux
- //使用redis存储session信息
- @EnableRedisWebSession
- public class AuthorityParentApplication {
-
- public static void main(String[] args) {
- Hooks.onOperatorDebug();
- SpringApplication.run(AuthorityParentApplication.class, args);
- }
- }
三、权限校验配置
先把主要的配置类信息分别说一下,后面会有组合后的configuration类,类名为:SecurityConfig
1、过滤器配置
webflux security配置调用链信息的主类为:ServerHttpSecurity,里面有OAuth2,formlogin等登陆方式的默认配置
管理系统的话我们启用FormLoginSpec即刻,就是传统的cookie配session的登陆验证机制
- /**
- * 此处的代码会放在SecurityConfig类中,此处只是摘要下,里面的handler在下文会详细介绍
- * @param http
- * @return
- */
- @Bean
- SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) {
- ServerHttpSecurity.FormLoginSpec formLoginSpec = http.formLogin();
- formLoginSpec.authenticationSuccessHandler(createAuthenticationSuccessHandler())
- .loginPage("/login")
- .authenticationFailureHandler(createAuthenticationFailureHandler());
- return formLoginSpec.and()
- .csrf().disable()
- .httpBasic().disable()
- .authorizeExchange()
- .pathMatchers(AUTH_WHITELIST).permitAll()
- .anyExchange().authenticated()
- .and().build();
- }
最后调用ServerHttpSecurity的build方法,从源码中可以查看构造的filter调用链,及配置信息等
- public SecurityWebFilterChain build() {
- if (this.built != null) {
- throw new IllegalStateException("This has already been built with the following stacktrace. " + buildToString());
- }
- this.built = new RuntimeException("First Build Invocation").fillInStackTrace();
- if (this.headers != null) {
- this.headers.configure(this);
- }
- WebFilter securityContextRepositoryWebFilter = securityContextRepositoryWebFilter();
- if (securityContextRepositoryWebFilter != null) {
- this.webFilters.add(securityContextRepositoryWebFilter);
- }
- if (this.httpsRedirectSpec != null) {
- this.httpsRedirectSpec.configure(this);
- }
- if (this.csrf != null) {
- this.csrf.configure(this);
- }
- if (this.cors != null) {
- this.cors.configure(this);
- }
- if (this.httpBasic != null) {
- this.httpBasic.authenticationManager(this.authenticationManager);
- this.httpBasic.configure(this);
- }
- if (this.formLogin != null) {
- this.formLogin.authenticationManager(this.authenticationManager);
- if (this.securityContextRepository != null) {
- this.formLogin.securityContextRepository(this.securityContextRepository);
- }
- this.formLogin.configure(this);
- }
- if (this.oauth2Login != null) {
- this.oauth2Login.configure(this);
- }
- if (this.resourceServer != null) {
- this.resourceServer.configure(this);
- }
- if (this.client != null) {
- this.client.configure(this);
- }
- this.loginPage.configure(this);
- if (this.logout != null) {
- this.logout.configure(this);
- }
- this.requestCache.configure(this);
- this.addFilterAt(new SecurityContextServerWebExchangeWebFilter(), SecurityWebFiltersOrder.SECURITY_CONTEXT_SERVER_WEB_EXCHANGE);
- if (this.authorizeExchange != null) {
- ServerAuthenticationEntryPoint authenticationEntryPoint = getAuthenticationEntryPoint();
- ExceptionTranslationWebFilter exceptionTranslationWebFilter = new ExceptionTranslationWebFilter();
- if (authenticationEntryPoint != null) {
- exceptionTranslationWebFilter.setAuthenticationEntryPoint(
- authenticationEntryPoint);
- }
- ServerAccessDeniedHandler accessDeniedHandler = getAccessDeniedHandler();
- if (accessDeniedHandler != null) {
- exceptionTranslationWebFilter.setAccessDeniedHandler(
- accessDeniedHandler);
- }
- this.addFilterAt(exceptionTranslationWebFilter, SecurityWebFiltersOrder.EXCEPTION_TRANSLATION);
- this.authorizeExchange.configure(this);
- }
- AnnotationAwareOrderComparator.sort(this.webFilters);
- List<WebFilter> sortedWebFilters = new ArrayList<>();
- this.webFilters.forEach( f -> {
- if (f instanceof OrderedWebFilter) {
- f = ((OrderedWebFilter) f).webFilter;
- }
- sortedWebFilters.add(f);
- });
- //sortedWebFilters中保存了登陆权限校验的所有filter信息
- sortedWebFilters.add(0, new ServerWebExchangeReactorContextWebFilter());
- return new MatcherSecurityWebFilterChain(getSecurityMatcher(), sortedWebFilters);
- }
2、自定义用户信息获取方式
获取用户信息的接口定义是:ReactiveUserDetailsService,官网的实现是:MapReactiveUserDetailsService,给出demo中是代码写死用户名密码、角色,并用此类存储。但实际中更多的从数据库中获取配置的用户角色信息,所以我们实现ReactiveUserDetailsService接口,自定义从mysql中获取用户信息的类,代码如下:
- import com.sisheng.authority.common.enums.ErrorEnum;
- import com.sisheng.authority.common.exception.BusinessException;
- import com.sisheng.authority.repository.user.entity.SystemUser;
- import com.sisheng.authority.servi