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;
}
}
2.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));
}
3.创建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;
}
4.重写org.springframework.social.oauth2.OAuth2Template#postForAccessGrant,定制AccessGrant5.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标准的