spring-security-oauth2 中優雅的擴展自定義(短信驗證碼)登錄方式-系列3


發送驗證碼的Controller

首先我們需要創建一個發送驗證碼的 Controller, 至於如何實現,這里就不多說了,大家都會的,本篇重點說明驗證部分.
【注意】在認證服務器上增加自己的 Controller, 默認情況下訪問是返回403,有兩種辦法解決:

  1. 把認證服務器也配制為資源服務器,既: 它既是認證服務器,也是資源服務器.並配制新增的Controller為任何人都能訪問
  2. 關閉認證服務器的 csrf.

由於我在認證服務器上增加的都是認證相關的功能,任何人都能訪問,不需要資源保護,所以我選擇了第二種方法.

關閉認證服務器的 csrf

在配制認證服務器的時候,我們創建過一個org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter 的子類用於安全配制. 如果你當時沒有配制,增加一個就行.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
	@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();  // 關閉 csrf
    }
}

  

增加短信驗證碼的 TokenGranter

參考 org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter 的代碼

 1 import org.apache.commons.lang3.StringUtils;
 2 import org.springframework.core.env.Environment;
 3 import org.springframework.security.authentication.AbstractAuthenticationToken;
 4 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 5 import org.springframework.security.core.Authentication;
 6 import org.springframework.security.core.userdetails.UserDetails;
 7 import org.springframework.security.core.userdetails.UserDetailsChecker;
 8 import org.springframework.security.core.userdetails.UserDetailsService;
 9 import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
10 import org.springframework.security.oauth2.provider.ClientDetails;
11 import org.springframework.security.oauth2.provider.ClientDetailsService;
12 import org.springframework.security.oauth2.provider.OAuth2Authentication;
13 import org.springframework.security.oauth2.provider.OAuth2Request;
14 import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
15 import org.springframework.security.oauth2.provider.TokenRequest;
16 import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
17 import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
18 
19 public class SMSCodeTokenGranter extends AbstractTokenGranter {
20 
21     private static final String GRANT_TYPE = "sms_code";
22 
23     public SMSCodeLoginTokenGranter(AuthorizationServerTokenServices tokenServices,
24         ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
25     super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
26     }
27 
28     @Override
29     protected OAuth2Authentication getOAuth2Authentication(ClientDetails client,
30         TokenRequest tokenRequest) {
31     
32     Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
33     String userMobileNo = parameters.get("username");  //客戶端提交的用戶名
34     String smscode = parameters.get("smscode");  //客戶端提交的驗證碼
35     
36     // 從庫里查用戶
37     UserDetails user = 從庫里查找用戶的代碼略;
38     if(user == null) {
39         throw new InvalidGrantException("用戶不存在");
40     }
41     
42     驗證用戶狀態(是否警用等),代碼略
43 
44     // 驗證驗證碼
45     String smsCodeCached = 獲取服務中保存的用戶驗證碼,代碼略.一般我們是在生成好后放到緩存中
46     if(StringUtils.isBlank(smsCodeCached)) {
47         throw new InvalidGrantException("用戶沒有發送驗證碼");
48     }
49     if(!smscode.equals(smsCodeCached)) {
50         throw new InvalidGrantException("驗證碼不正確");
51     }else {
52         驗證通過后從緩存中移除驗證碼,代碼略
53     }
54 
55     
56     Authentication userAuth = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
57     // 關於user.getAuthorities(): 我們的自定義用戶實體是實現了 
58     // org.springframework.security.core.userdetails.UserDetails 接口的, 所以有 user.getAuthorities()
59     // 當然該參數傳null也行
60     ((AbstractAuthenticationToken) userAuth).setDetails(parameters);
61     
62     OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);        
63     return new OAuth2Authentication(storedOAuth2Request, userAuth);
64     }
65 
66 }

把 SMSCodeTokenGranter 加入到 CompositeTokenGranter 需要的 List 中

在上一篇中我們修改了 OAuth2AuthorizationServerConfig類,現在繼續修改:
我們在 getDefaultTokenGranters 方法中加入: getDefaultTokenGranters 的完整代碼:

private List<TokenGranter> getDefaultTokenGranters() {
	ClientDetailsService clientDetails = clientDetailsService();
	AuthorizationServerTokenServices tokenServices = tokenServices();
	AuthorizationCodeServices authorizationCodeServices = authorizationCodeServices();
	OAuth2RequestFactory requestFactory = requestFactory();

	List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>();
	tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices,
		authorizationCodeServices, clientDetails, requestFactory));
	tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory));
	ImplicitTokenGranter implicit = new ImplicitTokenGranter(tokenServices, clientDetails,
		requestFactory);
	tokenGranters.add(implicit);
	tokenGranters.add(
		new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory));
	if (authenticationManager != null) {
	    tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager,
		    tokenServices, clientDetails, requestFactory));
	}
	
	tokenGranters.add(new SMSCodeLoginTokenGranter(tokenServices, clientDetails, requestFactory, userDetailsService));
	return tokenGranters;
    }

  

其他代碼不用修改

如何使用我們新增的短信驗證碼方式?

我們調用 /oauth/token 進行認證的時候,有一個 grant_type 參數,我們把它的值改為 sms_code
password 參數可以不要了,新增一個 smscode 參數
當然,上面的 “sms_code” 和 “smscode” 也是可以修改的:
“sms_code” 對應 SMSCodeTokenGranter 中的靜態常量 GRANT_TYPE
“smscode” 對應 SMSCodeTokenGranter.getOAuth2Authentication 方法中的 parameters.get(“smscode”);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM