角色說明
- CustomAuthenticationFilter:構建Token類並交給AuthenticationProvider進行驗證,繼承自AbstractAuthenticationProcessingFilter,AbstractAuthenticationProcessingFilter為UsernamePasswordAuthenticationFilter的父類,封裝了登陸過程用到的常用內容及方法.也可以不繼承此類完核心功能即可.
- CustomAuthenticationProvider:對支持的token進行校驗與shiro中Realm類似,區別是Security將授權與認證合併了.
- CustomAuthenticationToken:自定義token,存儲自定義內容,程序中使用SecurityContextHolder.getContext().getAuthentication()來獲取
java配置
過濾器配置
public class CustomAuthenticationFilter extends AbstractAuthenticationProcessingFilter { public CustomAuthenticationFilter() { super(new AntPathRequestMatcher("/custom/**")); // setContinueChainBeforeSuccessfulAuthentication(true); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { CustomAuthenticationToken authentication = null; try { Enumeration<String> headers = request.getHeaders("secretKey"); String secretKey = headers.nextElement(); // 通過request中參數構建自定義token,與CustomAuthenticationProvider對應即可 authentication = new CustomAuthenticationToken(secretKey, secretKey); //設置附屬信息,sessionid,ip setDetails(request, authentication); //通過Provider驗證token authentication = (CustomAuthenticationToken) getAuthenticationManager().authenticate(authentication); // SecurityContextHolder.getContext().setAuthentication(authentication); } catch (Exception e) { throw new AuthenticationServiceException("secretKey認證失敗",e); } return authentication; } protected void setDetails(HttpServletRequest request, CustomAuthenticationToken authRequest) { authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); } }
說明
- 此示例是在請求頭中增加參數用於認證,並沒有後續的登陸成功處理所以設置了setContinueChainBeforeSuccessfulAuthentication
- setContinueChainBeforeSuccessfulAuthentication設置為true表示認證成功之後進入後續過濾,不走後續的登陸成功處理
- 需要手動將認證結果存入SecurityContextHolder中避免後續拿不到認證信息.
Provider 配置
@Component public class CustomAuthenticationProvider implements AuthenticationProvider { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { String secretKey = (String) authentication.getCredentials(); //自定義校驗邏輯 if(!"123".equals(secretKey)){ throw new InsufficientAuthenticationException("認證錯誤"); } Set<String> dbAuthsSet = new HashSet<String>(); dbAuthsSet.add("customUser"); Collection<? extends GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(dbAuthsSet.toArray(new String[0])); return new CustomAuthenticationToken(secretKey, secretKey, authorities); } @Override public boolean supports(Class<?> authentication) { return CustomAuthenticationToken.class.isAssignableFrom(authentication); } }
SecurityConfig配置
@Order(2) @Configuration public class CustomSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private CustomAuthenticationProvider customAuthenticationProvider; @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .antMatcher("/custom/**") .addFilterBefore(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER) .and().authorizeRequests().antMatchers("/custom/**").authenticated(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(customAuthenticationProvider); } @Bean public CustomAuthenticationFilter customAuthenticationFilter() throws Exception { CustomAuthenticationFilter filter = new CustomAuthenticationFilter(); filter.setAuthenticationManager(super.authenticationManagerBean()); return filter; } }
相關代碼
https://gitee.com/MeiJM/spring-cram/tree/master/customSecurity 中CustomSecurityConfig相關部分