SpringSecurity隨筆(2)-OAuth2協議

短信登陸

參考密碼登陸過程spring

  1. 編寫短信登陸過濾器,驗證短信驗證碼
  2. 編寫未認證得SmsAuthenticationToken
  3. 將未認證的SmsAuthenticationToken傳遞給AuthenticationManager
  4. 編寫一個SmsAuthenticationProvider
  5. 調用UserDetialsService獲取用戶信息

OAuth協議

OAUTH協議爲用戶資源的受權提供了一個安全的、開放而又簡易的標準。json

認證服務器:認證用戶身份,生成令牌。 資源服務器:保存用戶資源,驗證令牌。安全

受權模式

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

受權碼模式bash

Spring Social

核心組件

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

AbstractOAuth2ApiBinding:不一樣服務提供商的用戶信息微信

OAuth2Connection:封裝獲取到的用戶信息app

OAuth2ConnectionFactory:建立OAuth2Connection實例ide

ServiceProvider:調用OAuth2Template獲取用戶信息post

ApiAdapter:將獲取的用戶信息封裝爲標準的OAuth2Connectionui

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, MultiValueMap<String, 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標準的

相關文章
相關標籤/搜索