1. 程式人生 > 程式設計 >SpringSecurity隨筆(2)-OAuth2協議

SpringSecurity隨筆(2)-OAuth2協議

簡訊登入

參考密碼登入過程

  1. 編寫簡訊登入過濾器,驗證簡訊驗證碼
  2. 編寫未認證得SmsAuthenticationToken
  3. 將未認證的SmsAuthenticationToken傳遞給AuthenticationManager
  4. 編寫一個SmsAuthenticationProvider
  5. 呼叫UserDetialsService獲取使用者資訊

OAuth協議

OAUTH協議為使用者資源的授權提供了一個安全的、開放而又簡易的標準。

認證伺服器:認證使用者身份,生成令牌。 資源伺服器:儲存使用者資源,驗證令牌。

授權模式

  1. 授權碼模式
  2. 密碼模式
  3. 客戶端模式
  4. 簡化模式

授權碼模式

Spring Social

核心元件

OAuth2Template:OAuth協議核心流程的封裝

AbstractOAuth2ApiBinding:不同服務提供商的使用者資訊

OAuth2Connection:封裝獲取到的使用者資訊

OAuth2ConnectionFactory:建立OAuth2Connection例項

ServiceProvider:呼叫OAuth2Template獲取使用者資訊

ApiAdapter:將獲取的使用者資訊封裝為標準的OAuth2Connection

UsersConnectionRepository:應用使用者資訊和服務提供商使用者資訊的對映

SpringSocial元件

SpringSocialConfigurer:核心配置元件

SpringSocialConfigurer#configure:建立SocialAuthenticationFilter

  1. org.springframework.social.security.provider.OAuth2AuthenticationService#getAuthToken 取授權碼
public SocialAuthenticationToken getAuthToken(HttpServletRequest request,HttpServletResponse response) throws SocialAuthenticationRedirectException {
        //獲取授權碼
		String code = request.getParameter("code"
); //授權碼為空 導向認證伺服器,獲取授權碼 if (!StringUtils.hasText(code)) { OAuth2Parameters params = new OAuth2Parameters(); params.setRedirectUri(buildReturnToUrl(request)); setScope(request,params); params.add("state",generateState(connectionFactory,request)); addCustomParameters(params); throw new SocialAuthenticationRedirectException(getConnectionFactory().getOAuthOperations().buildAuthenticateUrl(params)); // 拼URL // 拿這授權碼換令牌 } else if (StringUtils.hasText(code)) { try { String returnToUrl = buildReturnToUrl(request); // 獲取token AccessGrant accessGrant = getConnectionFactory().getOAuthOperations().exchangeForAccess(code,returnToUrl,null); // TODO avoid API call if possible (auth using token would be fine) Connection<S> connection = getConnectionFactory().createConnection(accessGrant); return new SocialAuthenticationToken(connection,null); } catch (RestClientException e) { logger.debug("failed to exchange for access",e); return null; } } else { return null; } } 複製程式碼
  1. org.springframework.social.oauth2.OAuth2Template#exchangeForAccess 取token
public AccessGrant exchangeForAccess(String authorizationCode,String redirectUri,MultiValueMap<String,String> additionalParameters) {
		MultiValueMap<String,String> params = new LinkedMultiValueMap<String,String>();
		if (useParametersForClientAuthentication) {
			params.set("client_id",clientId);
			params.set("client_secret",clientSecret);
		}
		params.set("code",authorizationCode);
		params.set("redirect_uri",redirectUri);
		params.set("grant_type","authorization_code");
		if (additionalParameters != null) {
			params.putAll(additionalParameters);
		}
		return postForAccessGrant(accessTokenUrl,params);
 }
//取toknen json格式	
protected AccessGrant postForAccessGrant(String accessTokenUrl,String> parameters) {
		return extractAccessGrant(getRestTemplate().postForObject(accessTokenUrl,parameters,Map.class));
 }
複製程式碼
  1. 建立RestTemplate模板
protected RestTemplate createRestTemplate() {
		ClientHttpRequestFactory requestFactory = ClientHttpRequestFactorySelector.getRequestFactory();
		RestTemplate restTemplate = new RestTemplate(requestFactory);
		List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>(2);
		converters.add(new FormHttpMessageConverter());
		converters.add(new FormMapHttpMessageConverter());
		converters.add(new MappingJackson2HttpMessageConverter());
		restTemplate.setMessageConverters(converters);
		restTemplate.setErrorHandler(new LoggingErrorHandler());
		if (!useParametersForClientAuthentication) {
			List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
			if (interceptors == null) {   // defensively initialize list if it is null. (See SOCIAL-430)
				interceptors = new ArrayList<ClientHttpRequestInterceptor>();
				restTemplate.setInterceptors(interceptors);
			}
			interceptors.add(new PreemptiveBasicAuthClientHttpRequestInterceptor(clientId,clientSecret));
		}
		return restTemplate;
	}
複製程式碼
  1. 重寫org.springframework.social.oauth2.OAuth2Template#postForAccessGrant,定製AccessGrant

  2. org.springframework.social.security.SocialAuthenticationProvider#authenticate 獲取到qq使用者資訊,進行許可權認證

org.springframework.social.security.SocialAuthenticationFilter#doAuthentication

QQ登入和微信登入的不同之處,QQ是拿到accessToken用accessToken去換openId,微信在返回accessToken的同時會返回opendId。

QQ在申請授權碼的時候通過OAuth2Template#buildAuthenticateUrl(OAuth2Parameters)拼裝了URL

例如:https://graph.qq.com/oauth2.0/authorize? client_id=101087& response_type=code& redirect_uri=http://www.sdasda.cn/qqLogin/qq& state=9c103c5a-34a8-4bf5-82df-c0eca91e8f4a

微信的授權碼url不是OAuth2標準的