SS常見的使用場景之一就是表單登錄了,而登錄校驗主要是經過過濾器來實現的。這裏的代碼使用了過濾器設計模式,將一系列的過濾器按順序準備好,依次進行過濾。下面來看下具體的實現流程吧!html
首先看下時序圖,直觀感覺下:java
稍微細緻點的:web
大體說明下:spring
1:最左側是登錄請求達到SS,進入過濾器鏈FilterChainProxysql
2:被處理登錄請求的過濾器AbstractAuthenticationProcessingFilter獲取,該過濾器最終的目的是生成一個表明登錄用戶的權限憑證(Authentication):UsernamePasswordAuthenticationToken數據庫
3:過濾器調用AuthenticationManager,權限管理者,它來管理Authentication,設計模式
4:權限管理者的管理方式是經過權限提供者提供的:AbstractUserDetailsAuthenticationProvider,它是用來獲取用戶及權限的,並判斷是否合法api
5:默認的獲取用戶及權限的方式是經過UserDetailsService接口定義,從命名來看,是爲User服務的,因爲默認的是從DB獲取,所裏這裏調用的就是其默認的實現類JdbcDaoImpl,即從DB獲取安全
6:獲取完畢以後,層層返回並判斷,最終符合要求就建立一個真正的Authentication,這就表明了登錄用戶及權限session
下面就來跟源碼一塊兒分享下,
看看過濾器鏈的核心:FilterChainProxy
它內部包含一個內部類VirtualFilterChain:
VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters); vfc.doFilter(fwRequest, fwResponse);
這裏的過濾方法是調用此內部類的doFilter()方法:
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if (currentPosition == size) { if (logger.isDebugEnabled()) { logger.debug(UrlUtils.buildRequestUrl(firewalledRequest) + " reached end of additional filter chain; proceeding with original chain"); } // Deactivate path stripping as we exit the security filter chain this.firewalledRequest.reset(); originalChain.doFilter(request, response); } else { currentPosition++; Filter nextFilter = additionalFilters.get(currentPosition - 1); if (logger.isDebugEnabled()) { logger.debug(UrlUtils.buildRequestUrl(firewalledRequest) + " at position " + currentPosition + " of " + size + " in additional filter chain; firing Filter: '" + nextFilter.getClass().getSimpleName() + "'"); } nextFilter.doFilter(request, response, this); } }
能夠看到,currentPosition表明當前處理的過濾器鏈List additionalFilters的索引,由第一個開始,若未到達最後一個過濾器,則currentPosition++,繼續處理
debug看到過濾器鏈以下:
[org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@5562047f, org.springframework.security.web.context.SecurityContextPersistenceFilter@53956e2d, org.springframework.security.web.header.HeaderWriterFilter@2477dc48, org.springframework.security.web.authentication.logout.LogoutFilter@170b08a9, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@aacdd0c, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@4e049f68, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@2aaa4fde, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@13079b81, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@394af762, org.springframework.security.web.session.SessionManagementFilter@3c3b6e37, org.springframework.security.web.access.ExceptionTranslationFilter@6567439d, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@5edb1d1f]
其中,這裏關注的是:UsernamePasswordAuthenticationFilter
這裏調用它的父類AbstractAuthenticationProcessingFilter的doFilter():
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; if (!requiresAuthentication(request, response)) { chain.doFilter(request, response); return; } if (logger.isDebugEnabled()) { logger.debug("Request is to process authentication"); } Authentication authResult; try { //調用權限驗證,返回的是Authentication對象 authResult = attemptAuthentication(request, response); if (authResult == null) { // return immediately as subclass has indicated that it hasn't completed // authentication return; } sessionStrategy.onAuthentication(authResult, request, response); } catch (InternalAuthenticationServiceException failed) { logger.error( "An internal error occurred while trying to authenticate the user.", failed); unsuccessfulAuthentication(request, response, failed); return; } catch (AuthenticationException failed) { // Authentication failed unsuccessfulAuthentication(request, response, failed); return; } // Authentication success if (continueChainBeforeSuccessfulAuthentication) { chain.doFilter(request, response); } successfulAuthentication(request, response, chain, authResult); }
主要調用的是authResult = attemptAuthentication(request, response)方法,該方法定義:
public abstract Authentication attemptAuthentication
該類是須要被子類重寫的,也就是UsernamePasswordAuthenticationFilter的attemptAuthentication:
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (postOnly && !request.getMethod().equals("POST")) { throw new AuthenticationServiceException( "Authentication method not supported: " + request.getMethod()); } String username = obtainUsername(request); String password = obtainPassword(request); if (username == null) { username = ""; } if (password == null) { password = ""; } username = username.trim(); //建立一個Authentication實例 UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( username, password); // Allow subclasses to set the "details" property setDetails(request, authRequest); //獲取AuthenticationManager,這裏就是Spring啓動的時候new出來的ProviderManager return this.getAuthenticationManager().authenticate(authRequest); }
這裏的UsernamePasswordAuthenticationToken是一個權限的憑證:
包括了用戶名,密碼,ip,sessionId,是否已受權過一級權限集合。
繼續調用ProviderManager的authenticate()方法,以下:
public Authentication authenticate(Authentication authentication) throws AuthenticationException { Class<? extends Authentication> toTest = authentication.getClass(); AuthenticationException lastException = null; Authentication result = null; boolean debug = logger.isDebugEnabled(); for (AuthenticationProvider provider : getProviders()) { if (!provider.supports(toTest)) { continue; } try { //調用provider的authenticate,這裏能夠自定義擴展 result = provider.authenticate(authentication); if (result != null) { copyDetails(authentication, result); break; } } ... } if (result == null && parent != null) { // Allow the parent to try. try { result = parent.authenticate(authentication); } catch (AuthenticationException e) { lastException = e; } } if (result != null) { //認證成功,清除密碼等信息 if (eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) { ((CredentialsContainer) result).eraseCredentials(); } eventPublisher.publishAuthenticationSuccess(result); return result; } prepareException(lastException, authentication); throw lastException; }
經過判斷髮現,調用了result = parent.authenticate(authentication);即ProviderManager自己,它包含的provider爲[org.springframework.security.authentication.dao.DaoAuthenticationProvider],這裏是能夠自定義擴展的
這裏調用的是DaoAuthenticationProvider父類AbstractUserDetailsAuthenticationProvider的authenticate()方法:
public Authentication authenticate(Authentication authentication) throws AuthenticationException { // Determine username String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName(); boolean cacheWasUsed = true; UserDetails user = this.userCache.getUserFromCache(username); if (user == null) { cacheWasUsed = false; try { //調用DaoAuthenticationProvider的獲取用戶方法 user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); } catch (UsernameNotFoundException notFound) { //看到這裏的異常轉換,統一轉換成了BadCredentialsException //能夠經過在子類中重寫父類的屬性去掉轉換:super.setHideUserNotFoundExceptions(false) logger.debug("User '" + username + "' not found"); if (hideUserNotFoundExceptions) { throw new BadCredentialsException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } else { throw notFound; } } Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract"); } try { //這裏已經獲取到DB中的用戶,判斷用戶是否鎖定,啓用,過時 preAuthenticationChecks.check(user); //調用DaoAuthenticationProvider,判斷用戶的密碼是否正確 additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); } catch (AuthenticationException exception) { if (cacheWasUsed) { // There was a problem, so try again after checking // we're using latest data (i.e. not from the cache) cacheWasUsed = false; user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); preAuthenticationChecks.check(user); additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); } else { throw exception; } } postAuthenticationChecks.check(user); if (!cacheWasUsed) { this.userCache.putUserInCache(user); } Object principalToReturn = user; if (forcePrincipalAsString) { principalToReturn = user.getUsername(); } return createSuccessAuthentication(principalToReturn, authentication, user); }
這裏調用了DaoAuthenticationProvider的retrieveUser()方法,返回一個UserDetail實例
UserDetails loadedUser; try { //獲取UserDetailService,能夠擴展爲本身的獲取用戶的方法 //要注意這裏僅僅傳入了username,獲取到用戶以後難以進行其餘操做了,返回的用戶就是SS框架中使用的用戶了 loadedUser = this.getUserDetailsService().loadUserByUsername(username); } catch (UsernameNotFoundException notFound) { if (authentication.getCredentials() != null) { String presentedPassword = authentication.getCredentials().toString(); passwordEncoder.isPasswordValid(userNotFoundEncodedPassword, presentedPassword, null); } throw notFound; }
這裏看到這裏是調用了UserDetailsService實現類,若是沒有擴展,這默認使用JdbcDaoImpl,它同時也繼承了JdbcDaoSupport,基本是基於Spring Data JPA實現的DB查詢:
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { List<UserDetails> users = loadUsersByUsername(username); if (users.size() == 0) { logger.debug("Query returned no results for user '" + username + "'"); throw new UsernameNotFoundException(messages.getMessage( "JdbcDaoImpl.notFound", new Object[] { username }, "Username {0} not found")); } UserDetails user = users.get(0); // contains no GrantedAuthority[] Set<GrantedAuthority> dbAuthsSet = new HashSet<GrantedAuthority>(); if (enableAuthorities) { //從DB中將該用戶相關聯的[權限]查詢出來並存儲 dbAuthsSet.addAll(loadUserAuthorities(user.getUsername())); } if (enableGroups) { //從DB中將該用戶相關聯的[組權限]查詢出來並存儲 dbAuthsSet.addAll(loadGroupAuthorities(user.getUsername())); } List<GrantedAuthority> dbAuths = new ArrayList<GrantedAuthority>(dbAuthsSet); //添加自定義的權限,須要子類本身實現 addCustomAuthorities(user.getUsername(), dbAuths); if (dbAuths.size() == 0) { logger.debug("User '" + username + "' has no authorities and will be treated as 'not found'"); throw new UsernameNotFoundException(messages.getMessage( "JdbcDaoImpl.noAuthority", new Object[] { username }, "User {0} has no GrantedAuthority")); } //new出一個UserDetail實例 return createUserDetails(username, user, dbAuths); }
通常狀況下能夠擴展UserDetailsService,實現本身的獲取用戶,權限,建立用戶的邏輯,該方法只是須要返回一個UserDetail實例
看下JdbcDaoImpl查詢用戶方法:
protected List<UserDetails> loadUsersByUsername(String username) { return getJdbcTemplate().query(usersByUsernameQuery, new String[] { username }, new RowMapper<UserDetails>() { public UserDetails mapRow(ResultSet rs, int rowNum) throws SQLException { String username = rs.getString(1); String password = rs.getString(2); boolean enabled = rs.getBoolean(3); return new User(username, password, enabled, true, true, true, AuthorityUtils.NO_AUTHORITIES); } }); }
其中查詢用戶的sql已在class中指定了:
public static final String DEF_USERS_BY_USERNAME_QUERY = "select username,password,enabled " + "from users " + "where username = ?";
這裏默認從DB中獲取到了一個用戶以下:
通過最後的createUserDetails(username, user, dbAuths)返回一個全新的用戶實例:
能夠看到,查詢出的權限已經被set到了用戶屬性中
返回用戶以後,繼續回到AbstractUserDetailsAuthenticationProvider的authenticate()方法,上面源碼中已標註了繼續進行用戶判斷,包括是否啓用,鎖定,過時:
preAuthenticationChecks.check(user)
能夠看到用戶屬性全都是true,校驗經過(建立UserDetail時默認的都是true)!
繼續判斷密碼是否正確:
additionalAuthenticationChecks(user,
(UsernamePasswordAuthenticationToken) authentication)
這個方法的定義:protected abstract void additionalAuthenticationChecks
仍然是須要被子類繼承重寫的
因爲這裏沒有擴展DaoAuthenticationProvider類,因此這裏就調用了子類DaoAuthenticationProvider的方法:
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { Object salt = null; //鹽值 if (this.saltSource != null) { salt = this.saltSource.getSalt(userDetails); } if (authentication.getCredentials() == null) { logger.debug("Authentication failed: no credentials provided"); throw new BadCredentialsException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } String presentedPassword = authentication.getCredentials().toString(); //判斷密碼是否有效 if (!passwordEncoder.isPasswordValid(userDetails.getPassword(), presentedPassword, salt)) { logger.debug("Authentication failed: password does not match stored value"); throw new BadCredentialsException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } }
這裏沒有重寫PasswordEncoder,SS使用了默認的加解密,最終經過:
BCrypt.checkpw(rawPassword.toString(), encodedPassword)實現
校驗經過
走到這裏,AbstractUserDetailsAuthenticationProvider的authenticate()方法即將執行結束,用戶,權限已經查詢出來了,屬性,密碼等也校驗了,可是該方法要返回一個Authentication對象,這裏還未執行。
由前所述,在最開始的UsernamePasswordAuthenticationFilter中已經經過登陸的用戶名,密碼等信息建立一個Authentication:UsernamePasswordAuthenticationToken,可是這個權限對象是未通過權限認證的,是不可靠的,因此要對它進行更新,也就是最後一句:
return createSuccessAuthentication(principalToReturn, authentication, user);
protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) { // Ensure we return the original credentials the user supplied, // so subsequent attempts are successful even with encoded passwords. // Also ensure we return the original getDetails(), so that future // authentication events after cache expiry contain the details UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken( principal, authentication.getCredentials(), authoritiesMapper.mapAuthorities(user.getAuthorities())); result.setDetails(authentication.getDetails()); return result; }
這裏就是經過查詢出的信息從新建立了一個Authentication:UsernamePasswordAuthenticationToken對象,並返回!
至此,在ProviderManager中的權限校驗方法已經執行完畢,SS框架已經拿到了真實的登錄用戶了
而後執行密碼等憑證清除方法,保證安全,最後進行認證成功的事件發佈
走到這裏,過濾器鏈基本就執行完畢了,後續繼續進行doFilter以後的方法了
--------------------
我這邊也簡單的擴展了常見類,用於實現一個權限API接口驗證:
首先是啓動配置類:
@Order(99) @Profile("auth") @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) static class SpringSecurityConfigurer extends WebSecurityConfigurerAdapter { @Autowired private JdbcUserDetailsManager userDetailsManager; @Autowired private UserRepository userRepository; @Value("${umUrl}") private String umUrl; public static final String USER_ROLE = "user"; @Override protected void configure(HttpSecurity http) throws Exception { UmAuthenticationFailureHandler failHandler = new UmAuthenticationFailureHandler(); http.csrf().disable(); http.headers().frameOptions().sameOrigin(); http.authorizeRequests() .antMatchers("/openapi/**", "/vendor/**", "/styles/**", "/scripts/**", "/views/**", "/img/**") .permitAll() .antMatchers("/**") .hasAnyRole(USER_ROLE); http.formLogin() .loginPage("/signin") .permitAll() .failureHandler(failHandler) // .failureUrl("/signin?#/error") .and() .httpBasic(); http.logout() .invalidateHttpSession(true) .clearAuthentication(true) .logoutSuccessUrl("/signin?#/logout"); http.exceptionHandling() .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/signin")); System.out.println(umUrl); //擴展登錄校驗 //userService UmUserDetailsService userService = new UmUserDetailsService(userDetailsManager,userRepository); //provider AuthenticationProvider umDaoAuthenticationProvider = new UmDaoAuthenticationProvider(userService,umUrl); // AuthenticationProvider umDaoAuthenticationProvider = new UmDaoAuthenticationProvider(userService,userDetailsManager); List<AuthenticationProvider> providers = new ArrayList<AuthenticationProvider>(); providers.add(umDaoAuthenticationProvider); //authManager // UmAuthenticationManager mamager = new UmAuthenticationManager(providers); // UmAuthenticationManager umAuthenticationManager = new UmAuthenticationManager(providers,mamager); UmAuthenticationManager umAuthenticationManager = new UmAuthenticationManager(providers); //filter UmUserPwdAuthenticationFilter umUserPwdAuthenticationFilter = new UmUserPwdAuthenticationFilter(umAuthenticationManager); //add filter to chain http.authenticationProvider(umDaoAuthenticationProvider).addFilterAfter(umUserPwdAuthenticationFilter,UmUserPwdAuthenticationFilter.class); } }
該類裏面主要是新增的校驗擴展類,最終造成一個過濾器並加入到過濾器鏈中
相關類以下:
public class UmUserPwdAuthenticationFilter extends UsernamePasswordAuthenticationFilter{ AuthenticationManager umAuthenticationManager = null; public UmUserPwdAuthenticationFilter (UmAuthenticationManager umAuthenticationManager) { this.umAuthenticationManager = umAuthenticationManager; setAuthenticationManager(umAuthenticationManager); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { System.out.println("--------------------MyUserPwdAuthenticationProvider-------------"); return super.attemptAuthentication(request, response); } }
public class UmAuthenticationManager extends ProviderManager{ public UmAuthenticationManager(List<AuthenticationProvider> providers) { super(providers); } public UmAuthenticationManager(List<AuthenticationProvider> providers,UmAuthenticationManager umAuthenticationManager) { super(providers,umAuthenticationManager); } @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { System.out.println("------------------------UmAuthenticationManager---------------------------"); return authentication; } }
@Service public class UmUserDetailsService implements UserDetailsService { protected final Log logger = LogFactory.getLog(getClass()); private JdbcUserDetailsManager userDetailsManager; private UserRepository userRepository; public UmUserDetailsService() { } public UmUserDetailsService(JdbcUserDetailsManager userDetailsManager, UserRepository userRepository) { this.userDetailsManager = userDetailsManager; this.userRepository = userRepository; } /** * 原本能夠調用框架的JdbcDaoImpl#loadUserByUsername從數據庫獲取用戶, * 可是若是登錄用戶沒有存儲到DB,那麼獲取到的用戶爲null,後續框架鑑權不過,就走不到UM鑑權 * 因此重寫了loadUserByUsername方法,考慮不在這裏進行DB查詢 * 但又因爲這裏重寫的方法參數只有username,沒有password,沒法進行UM鑑權,只能考慮往前重寫父類方法 可是protected final * UserDetails retrieveUser,是沒法繼承重寫的 因此這裏只能新建一個非空校驗用戶UserDetail,用以跨過框架的校驗 * 後續發現跨過框架校驗後,沒法將DB查出的用戶替換掉校驗用戶,因此只能在這裏查詢DB用戶 */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { logger.info("UmUserDetailsService#loadUserByUsername :username = " + username); // 查詢當前用戶是否存在於DB String usersByUsernameQuery = "select id,username,email,enabled from users where username = ?"; List<UserInfo> userList = userDetailsManager.getJdbcTemplate().query(usersByUsernameQuery, new String[] { username }, new RowMapper<UserInfo>() { public UserInfo mapRow(ResultSet rs, int rowNum) throws SQLException { int userId = rs.getInt(1); String username = rs.getString(2); String email = rs.getString(3); boolean enabled = rs.getBoolean(4); return new UserInfo(userId + "", username, email, enabled ? "1" : "0"); } }); // user exists if (userList != null && userList.size() > 0) { logger.info("UmUserDetailsService#loadUserByUsername get username from db success :username = " + username); return userList.get(0); } else { throw new InternalAuthenticationServiceException("NO_REGISTER:還沒有開通配置平臺帳號,請聯繫ZHUANGJIAJIE778開通"); } } public JdbcUserDetailsManager getUserDetailsManager() { return userDetailsManager; } public UserRepository getUserRepository() { return userRepository; } }
public class UmDaoAuthenticationProvider extends DaoAuthenticationProvider{ private PasswordEncoder encoder = new BCryptPasswordEncoder(); private JdbcUserDetailsManager userDetailsManager; private String umUrl; public UmDaoAuthenticationProvider(UmUserDetailsService umUserDetailsService,String umUrl) { super.setHideUserNotFoundExceptions(false); //禁止包裝異常 this.umUrl = umUrl; this.userDetailsManager = userDetailsManager; setUserDetailsService(umUserDetailsService); } // public UmDaoAuthenticationProvider(UmUserDetailsService umUserDetailsService,JdbcUserDetailsManager userDetailsManager) { // super.setHideUserNotFoundExceptions(false); // this.umUserDetailsService = umUserDetailsService; // this.userDetailsManager = userDetailsManager; // setUserDetailsService(umUserDetailsService); // } @SuppressWarnings("deprecation") protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { Object salt = null; // if (this.saltSource != null) { // salt = this.saltSource.getSalt(userDetails); // } if (authentication.getCredentials() == null) { logger.debug("Authentication failed: no credentials provided"); throw new BadCredentialsException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } String username = authentication.getPrincipal().toString(); String presentedPassword = authentication.getCredentials().toString(); logger.info("MyDaoAuthenticationProvider # additionalAuthenticationChecks : pppp = " + presentedPassword); if(username.equals("apollo")) { if("apolloadmin".equals(presentedPassword)) { logger.error("login success : no need to UM authenticate user : [username = " + username + "] : "); return; } else { logger.error("login fail : pwd error : [username = " + username + "] : "); throw new InternalAuthenticationServiceException("login fail[username = " + username + " ] : username or pwd error!"); } // } else if(username.equals("yangli1")) { // logger.error("UM鑑權失敗[username = " + username + "] : "); // throw new InternalAuthenticationServiceException("用戶名或者密碼錯誤---自定義"); } // logger.info("UM鑑權成功[username = " + username + "]"); //開始UM鑑權 String URL_INFO = umUrl + "/common/checkUUU.shtml"; // String URL_INFO = "http://localhost:8888/smp-cms-web/common/checkUmUser.shtml"; logger.info("UM authenticate url : [URL_INFO = " + URL_INFO + "]"); NameValuePair[] data = {new NameValuePair("uuu", authentication.getPrincipal().toString()), new NameValuePair("ppp", presentedPassword)}; try { Map<String, Object> resultMap = executeHttp(URL_INFO,data); if(resultMap.get("resultCode") != null && resultMap.get("resultCode").equals("00")) { logger.info("UM authenticate success : [username = " + username + "]"); } else { logger.error("UM authenticate fail : [username = " + username + "]"); throw new InternalAuthenticationServiceException("UM authenticate fail : [username = " + username + " ] : 用戶名或者密碼錯誤!"); } } catch (Exception e) { logger.error("UM authenticate fail : [username = " + username + "] : " + e.getMessage(),e); throw new InternalAuthenticationServiceException("UM authenticate fail : [username = " + username + " ] :" + " invoke um authenticate interface error : " + e.getMessage(),e); } // if (!passwordEncoder.isPasswordValid(userDetails.getPassword(), // presentedPassword, salt)) { // logger.debug("Authentication failed: password does not match stored value"); // // throw new BadCredentialsException(messages.getMessage( // "AbstractUserDetailsAuthenticationProvider.badCredentials", // "Bad credentials")); // } } protected Map<String, Object> executeHttp(String URL, NameValuePair[] data) throws Exception { Map<String, Object> resultMap = new HashMap<String, Object>(); logger.info("UmDaoAuthenticationProvider#executeHttp#url:[" + JSON.toJSONString(data) + "]"); logger.info("UmDaoAuthenticationProvider#executeHttp#address:[" + URL + "]"); String resBodyStr = ""; int code = 0; try { HttpClient httpClient = new HttpClient(); PostMethod postMethod = new PostMethod(URL); postMethod.setRequestBody(data); postMethod.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "UTF-8"); httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(10000); httpClient.getHttpConnectionManager().getParams().setSoTimeout(10000); code = httpClient.executeMethod(postMethod);// 執行postMethod,返回狀態碼 resBodyStr = postMethod.getResponseBodyAsString(); logger.info("UmDaoAuthenticationProvider#executeHttp#responseCode:[" + code + "]#responseMsg:[:" + resBodyStr + "]"); // 狀態碼爲200時,成功請求,其中返回CODE非00,均表示不正常 if (code == 200 && resBodyStr != null) { Map<String, Object> map = JSON.parseObject(resBodyStr, Map.class); resultMap.put("resultCode", map.get("CODE")); resultMap.put("resultMsg", map.get("MSG")); if (map.get("CODE").equals("00")) { resultMap.put("resultData", map.get("DATA")); } } else { resultMap.put("resultCode", "01"); resultMap.put("resultMsg", "invoke um authenticate interface error , contact ITer plz"); } } catch (Exception e) { e.printStackTrace(); logger.error("UmDaoAuthenticationProvider#executeHttp#exception#code[" + code + "]#resBodyStr[:" + resBodyStr + "]" + e.getMessage(),e); resultMap.put("resultCode", "01"); resultMap.put("resultMsg", "invoke um authenticate interface error , contact ITer plz"); } return resultMap; } }
public class UmAuthenticationFailureHandler implements AuthenticationFailureHandler{ protected final Log logger = LogFactory.getLog(getClass()); private String defaultFailureUrl = "/signin?#/error"; private static String NO_REGISTER = "NO_REGISTER"; private boolean forwardToDestination = false; private boolean allowSessionCreation = true; private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); /** * Performs the redirect or forward to the {@code defaultFailureUrl} if set, otherwise * returns a 401 error code. * <p> * If redirecting or forwarding, {@code saveException} will be called to cache the * exception for use in the target view. */ public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { if (defaultFailureUrl == null) { logger.debug("No failure URL set, sending 401 Unauthorized error"); response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed: " + exception.getMessage()); } else { saveException(request, exception); //初始化 defaultFailureUrl = "/signin?#/error"; //未開通帳號的錯誤 if(exception.getMessage().startsWith(NO_REGISTER)) { defaultFailureUrl = defaultFailureUrl + "="+ NO_REGISTER; } if (forwardToDestination) { logger.debug("Forwarding to " + defaultFailureUrl); request.getRequestDispatcher(defaultFailureUrl).forward(request, response); } else { logger.debug("Redirecting to " + defaultFailureUrl); redirectStrategy.sendRedirect(request, response, defaultFailureUrl); } } } /** * Caches the {@code AuthenticationException} for use in view rendering. * <p> * If {@code forwardToDestination} is set to true, request scope will be used, * otherwise it will attempt to store the exception in the session. If there is no * session and {@code allowSessionCreation} is {@code true} a session will be created. * Otherwise the exception will not be stored. */ protected final void saveException(HttpServletRequest request, AuthenticationException exception) { if (forwardToDestination) { request.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception); } else { HttpSession session = request.getSession(false); if (session != null || allowSessionCreation) { request.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception); } } } }
實現了功能,可是還沒優化,能夠參考下