最近项目上运用到了spring security来控制控制权限,然而钉钉端采用的前后端分离的模式进行开发,pc端未采用前后端分离。这样就出现了两个问题:
1.钉钉前端无法携带JSSESSIONID,从而导致spring security框架无法自动解析权限,需要自行实现token并存入redis。
2.钉钉端需要使用手机号码免密登录,pc端需要账号密码进行登录。
功能完成后,此处做个总结。
在SecurityConfig类中注册认证提供者
pc端我采用的是spring security默认的DaoAuthenticationProvider 从数据库中获取账号密码进行比对登录,将userDetailService的实现类改为了自己业务中的实现类。
钉钉端采用的是自行维护相关验证逻辑。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private HttpStatusEntryPoint httpStatusEntryPoint = new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED);
/**
* 成功的处理器
*/
@Autowired
private CsoftAuthenticationSuccessHandler csoftAuthenticationSuccessHandler;
@Autowired
private CsoftUserDetailService csoftUserDetailService;
@Autowired
private TokenAuthenticationProvider tokenAuthenticationProvider;
/**
* 将认证器注册到认证管理器中
* @param auth
* @throws Exception
*/
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//此处将认证提供者注册到管理中心中统一管理
auth.authenticationProvider(tokenAuthenticationProvider)//自实现token的手机号码登录校验
.authenticationProvider(daoAuthenticationProvider())//springsecurity 自带的账号密码登录校验
;
}
/**
* 失败的处理器
*/
@Autowired
private CsoftAuthenticationFailureHandler csoftAuthenticationFailureHandler;
/**
* 注入密码加密对象
* @return Spring原生的加密对象Bean
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 将默认的认证中心中的属性 改为业务中的实现类
* @return
*/
@Bean
DaoAuthenticationProvider daoAuthenticationProvider(){
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
daoAuthenticationProvider.setUserDetailsService(csoftUserDetailService);
return daoAuthenticationProvider;
}
/**
* 验证码拦截器
*/
@Autowired
private ValidateCodeFilter validateCodeFilter;
//注册自定义的UsernamePasswordAuthenticationFilter
@Bean
CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
filter.setFilterProcessesUrl(SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM);
//这句很关键,重用WebSecurityConfigurerAdapter配置的AuthenticationManager,不然要自己组装AuthenticationManager
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationSuccessHandler(csoftAuthenticationSuccessHandler);
filter.setAuthenticationFailureHandler(csoftAuthenticationFailureHandler);
return filter;
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* 请求安全策略
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// 跨域攻击防护
http.cors().and().csrf().disable();
// 同源打开iframe
http.headers().frameOptions().sameOrigin();
/**
* 通过表单用户名和密码登入验证
*/
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.formLogin().loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL)
.loginProcessingUrl(SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM)
.successHandler(csoftAuthenticationSuccessHandler)
.failureHandler(csoftAuthenticationFailureHandler)
.and()
.authorizeRequests()
.antMatchers("/account/**").hasAnyAuthority("ROLE_USER")
.antMatchers("/admin/**").hasAnyAuthority("ROLE_ADMIN")
.antMatchers(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL).permitAll()
.anyRequest().authenticated();
//钉钉端过滤器
AuthenticationFilter filter = new AuthenticationFilter(authenticationManager(),