重寫了UsernamePasswordAuthenticationFilter,裏面繼承AbstractAuthenticationProcessingFilter,這個類裏面的session認證策略,是一個空方法,貌似RememberMe也是.css
public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean implements ApplicationEventPublisherAware, MessageSourceAware { protected ApplicationEventPublisher eventPublisher; protected AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource(); private AuthenticationManager authenticationManager; protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); private RememberMeServices rememberMeServices = new NullRememberMeServices(); private RequestMatcher requiresAuthenticationRequestMatcher; private boolean continueChainBeforeSuccessfulAuthentication = false; private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy(); private boolean allowSessionCreation = true; private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler(); private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
NullAuthenticatedSessionStrategy 源碼
public final class NullAuthenticatedSessionStrategy implements SessionAuthenticationStrategy { public NullAuthenticatedSessionStrategy() { } public void onAuthentication(Authentication authentication, HttpServletRequest request, HttpServletResponse response) { } }
因此必須本身配置一個session驗證策略,以及配置併發控制.紅字爲關鍵web
WebSecurityConfigurerAdapter
/** * Created by ZhenWeiLai on on 2016-10-16. * <p> * 三種方法級權限控制 * <p> * 1.securedEnabled: Spring Security’s native annotation * 2.jsr250Enabled: standards-based and allow simple role-based constraints * 3.prePostEnabled: expression-based */ @EnableWebSecurity //@EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Resource private UserDetailsService userDetailsService; @Resource private FilterInvocationSecurityMetadataSource securityMetadataSource; @Resource private SessionRegistry sessionRegistry; @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/assets/**"); web.ignoring().antMatchers("/components/**"); web.ignoring().antMatchers("/css/**"); web.ignoring().antMatchers("/images/**"); web.ignoring().antMatchers("/js/**"); web.ignoring().antMatchers("/mustache/**"); web.ignoring().antMatchers("/favicon.ico"); //註冊地址不攔截 // web.ignoring().antMatchers("/base/invoice/userinfo/u/reg"); // web.ignoring().antMatchers("/**"); } @Override protected void configure(HttpSecurity http) throws Exception { //解決不容許顯示在iframe的問題 http.headers().frameOptions().disable(); http.addFilterAt(usernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); //session併發控制過濾器 http.addFilterAt(new ConcurrentSessionFilter(sessionRegistry,sessionInformationExpiredStrategy()),ConcurrentSessionFilter.class); //自定義過濾器 //在適當的地方加入 http.addFilterAt(filterSecurityInterceptor(securityMetadataSource, accessDecisionManager(), authenticationManagerBean()), FilterSecurityInterceptor.class); http.exceptionHandling().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")).and().logout().logoutUrl("/logout").logoutSuccessUrl("/login").permitAll().and().exceptionHandling().accessDeniedPage("/accessDenied"); http.authorizeRequests().anyRequest().fullyAuthenticated(); // 關閉csrf http.csrf().disable(); /** * 如下配置無效 */ //session管理 //session失效後跳轉 // http.sessionManagement().invalidSessionUrl("/login"); // //只容許一個用戶登陸,若是同一個帳戶兩次登陸,那麼第一個帳戶將被踢下線,跳轉到登陸頁面 // http.sessionManagement().maximumSessions(1).expiredUrl("/login"); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 自定義UserDetailsService,設置加密算法 auth.userDetailsService(userDetailsService); //.passwordEncoder(passwordEncoder()) //不刪除憑據,以便記住用戶 auth.eraseCredentials(false); } //session失效跳轉 private SessionInformationExpiredStrategy sessionInformationExpiredStrategy() { return new SimpleRedirectSessionInformationExpiredStrategy("/login"); } @Bean public SessionRegistry sessionRegistry() { return new SessionRegistryImpl(); } //SpringSecurity內置的session監聽器 @Bean public HttpSessionEventPublisher httpSessionEventPublisher() { return new HttpSessionEventPublisher(); } private UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() throws Exception { UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter = new CuzUsernamePasswordAuthenticationFilter(); usernamePasswordAuthenticationFilter.setPostOnly(true); usernamePasswordAuthenticationFilter.setAuthenticationManager(this.authenticationManager()); usernamePasswordAuthenticationFilter.setUsernameParameter("name_key"); usernamePasswordAuthenticationFilter.setPasswordParameter("pwd_key"); usernamePasswordAuthenticationFilter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/checkLogin", "POST")); usernamePasswordAuthenticationFilter.setAuthenticationFailureHandler(simpleUrlAuthenticationFailureHandler()); usernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler()); //session併發控制,由於默認的併發控制方法是空方法.這裏必須本身配置一個 usernamePasswordAuthenticationFilter.setSessionAuthenticationStrategy(new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry)); return usernamePasswordAuthenticationFilter; } // @Bean // public LoggerListener loggerListener() { // System.out.println("org.springframework.security.authentication.event.LoggerListener"); // return new LoggerListener(); // } // // @Bean // public org.springframework.security.access.event.LoggerListener eventLoggerListener() { // System.out.println("org.springframework.security.access.event.LoggerListener"); // return new org.springframework.security.access.event.LoggerListener(); // } /** * 投票器 */ private AbstractAccessDecisionManager accessDecisionManager() { List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList(); decisionVoters.add(new AuthenticatedVoter()); decisionVoters.add(new RoleVoter());//角色投票器,默認前綴爲ROLE_ RoleVoter AuthVoter = new RoleVoter(); AuthVoter.setRolePrefix("AUTH_");//特殊權限投票器,修改前綴爲AUTH_ decisionVoters.add(AuthVoter); AbstractAccessDecisionManager accessDecisionManager = new AffirmativeBased(decisionVoters); return accessDecisionManager; } @Override public AuthenticationManager authenticationManagerBean() { AuthenticationManager authenticationManager = null; try { authenticationManager = super.authenticationManagerBean(); } catch (Exception e) { e.printStackTrace(); } return authenticationManager; } /** * 驗證異常處理器 * * @return */ private SimpleUrlAuthenticationFailureHandler simpleUrlAuthenticationFailureHandler() { return new SimpleUrlAuthenticationFailureHandler("/getLoginError"); } // /** // * 表達式控制器 // * // * @return // */ // private DefaultWebSecurityExpressionHandler webSecurityExpressionHandler() { // DefaultWebSecurityExpressionHandler webSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler(); // return webSecurityExpressionHandler; // } // /** // * 表達式投票器 // * // * @return // */ // private WebExpressionVoter webExpressionVoter() { // WebExpressionVoter webExpressionVoter = new WebExpressionVoter(); // webExpressionVoter.setExpressionHandler(webSecurityExpressionHandler()); // return webExpressionVoter; // } // Code5 官方推薦加密算法 // @Bean("passwordEncoder") // public BCryptPasswordEncoder passwordEncoder() { // BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); // return bCryptPasswordEncoder; // } // // Code3---------------------------------------------- /** * 登陸成功後跳轉 * 若是須要根據不一樣的角色作不一樣的跳轉處理,那麼繼承AuthenticationSuccessHandler重寫方法 * * @return */ private SimpleUrlAuthenticationSuccessHandler authenticationSuccessHandler() { return new SimpleUrlAuthenticationSuccessHandler("/loginSuccess"); } /** * Created by ZhenWeiLai on on 2016-10-16. */ private FilterSecurityInterceptor filterSecurityInterceptor(FilterInvocationSecurityMetadataSource securityMetadataSource, AccessDecisionManager accessDecisionManager, AuthenticationManager authenticationManager) { FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor(); filterSecurityInterceptor.setSecurityMetadataSource(securityMetadataSource); filterSecurityInterceptor.setAccessDecisionManager(accessDecisionManager); filterSecurityInterceptor.setAuthenticationManager(authenticationManager); return filterSecurityInterceptor; } }
繼承UsernamePasswordAuthenticationFilter ,注入SessionRegistry ,當用戶登陸驗證成功後註冊session算法
/** * Created by ZhenWeiLai on 2017/6/2. */ public class CuzUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter { private boolean postOnly = true; @Resource private SessionRegistry sessionRegistry; @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if(this.postOnly && !request.getMethod().equals("POST")) { throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); } else { String username = this.obtainUsername(request); String password = this.obtainPassword(request); if(username == null) { username = ""; } if(password == null) { password = ""; } username = username.trim(); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); //用戶名密碼驗證經過後,註冊session sessionRegistry.registerNewSession(request.getSession().getId(),authRequest.getPrincipal()); this.setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } } }
重寫用戶實體類的比較方法.spring
/** * 重寫比較方法,SpringSecurity根據用戶名來比較是否同一個用戶 */ @Override public boolean equals(Object o){ if(o.toString().equals(this.username)) return true; return false; } @Override public int hashCode(){ return username.hashCode(); } @Override public String toString() { return this.username; }