在有些项目中一套系统可能有多套登录逻辑
比如
/manage/** 管理后台相关接口走后台认证
/h5/api/** 小程序相关接口走token认证
1.在"5.WebSecurityConfiguration首先会初始化一个webSecurity管理我们定义的WebSecurityConfigurerAdapter子类"我们可以看到先初始化WebSecurity 并将我们自定义配置SpringSecurity的WebSecurityConfigurerAdapter设置成WebSecurity的configures WebSecurityConfigurerAdapter本质也是一个builder
2.在”org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration#springSecurityFilterChain"处会调用WebSecurity.build方法构建springSecurityFilterChain
3.build内部先调用init,init方法会遍历configures也就是我们的WebSecurityConfiguration的init方法源码处<2>
3.1 WebSecurityConfigurerAdapter的init方法会初始化HttpSecurity 同时也会调用configure方法允许我们自定义HttpSecurityConfig和AuthenticationManagerBuilder源码处<8>
3.2最终将HttpSecurity(本质也是一个build) add到webSecurity的securityFilterChainBuilder源码处<6>
4.调用webSecurity.performBuild方法 会循环调用securityFilterChainBuilder也就是HttpSecurity的build方法构建出SecurityFilterChain add 到securityFilterChains
4.1SecurityFilterChain维护了一批Filter 以及这个SecurityFilterChain的匹配规则 比如/manage/** 或者/h5/api/** 默认是是AnyRequest
通过以上源码知识我们可以定义多个WebSecurityConfigurerAdapter实现生成多个SecurityFilterChain并且定义匹配规则
以下就是处理不同url的不同登录逻辑 HttpSessionSecurityContextRepository是因为源码Spring-security源码-Filter之SecurityContextPersistenceFilter(十一)
默认是HttpSessionSecurityContextRepository来负责读取和写入保存当前会话,但是key如果我们没有定义的话默认都是使用同一个SPRING_SECURITY_CONTEXT
UserSecurityConfig
@Configuration @Order(1) public class UserSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { /** * inMemoryAuthentication 开启在内存中定义用户 * 多个用户通过and隔开 */ auth.inMemoryAuthentication() .withUser("liqiang").password("liqiang").roles("admin") .and() .withUser("admin").password("admin").roles("admin"); } @Override protected void configure(HttpSecurity http) throws Exception { RequestMatcher requestMatcher=new AntPathRequestMatcher("/user/**"); HttpSessionSecurityContextRepository httpSessionSecurityContextRepository= new HttpSessionSecurityContextRepository(); httpSessionSecurityContextRepository.setSpringSecurityContextKey("user"); http.securityContext().securityContextRepository(httpSessionSecurityContextRepository).and(). requestMatcher(requestMatcher) .authorizeRequests() .anyRequest() .authenticated() .and().rememberMe()//记住登录 .tokenRepository(new InMemoryTokenRepositoryImpl()) .and() .formLogin()// rm表单的方式 .loginPage("/user/login")//登录页面路径 .loginProcessingUrl("/user/doLogin") //自定义登录请求地址 .defaultSuccessUrl("/user/hello") .usernameParameter("loginName") .passwordParameter("loginPassword") .permitAll(true)//不拦截 .and() .csrf()//记得关闭 .disable() .sessionManagement(). maximumSessions(1) .maxSessionsPreventsLogin(true); } }
ProductSecurityConfig
@Configuration @Order(0) public class ProductSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { /** * inMemoryAuthentication 开启在内存中定义用户 * 多个用户通过and隔开 */ auth.inMemoryAuthentication() .withUser("liqiang").password("liqiang").roles("admin") .and() .withUser("admin").password("admin").roles("admin"); } @Override protected void configure(HttpSecurity http) throws Exception { RequestMatcher requestMatcher=new AntPathRequestMatcher("/product/**"); HttpSessionSecurityContextRepository httpSessionSecurityContextRepository= new HttpSessionSecurityContextRepository(); httpSessionSecurityContextRepository.setSpringSecurityContextKey("product"); http.requestMatcher(requestMatcher).securityContext().securityContextRepository(httpSessionSecurityContextRepository).and().authorizeRequests() .anyRequest() .authenticated() .and().rememberMe()//记住登录 .tokenRepository(new InMemoryTokenRepositoryImpl()) .and() .formLogin()// rm表单的方式 .loginPage("/product/login")//登录页面路径 .loginProcessingUrl("/product/doLogin") //自定义登录请求地址 .defaultSuccessUrl("/product/hello") .usernameParameter("loginName") .passwordParameter("loginPassword") .permitAll(true)//不拦截 .and() .csrf()//记得关闭 .disable() .sessionManagement(). maximumSessions(1) .maxSessionsPreventsLogin(true); } }