SpringSecurity-2-表單登錄流程

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);
			}
		}
	}

}

實現了功能,可是還沒優化,能夠參考下

相關文章
相關標籤/搜索