在做自己的产品时,为了更好的用户体验,在登录注册这一环节,我们现在越来越多的使用社交登录(QQ、微信、FB等)。为了规范社交账号登录出现OAuth协议。
一、OAuth在我们使用一个第三方的APP(Client_App),点击使用社交应用登录时,首先会离开Client_App调到社交应用,在社交应用上会显示一个授权界面,点击确定授权后又会从社交应用调回到Client_App中并且登录成功。这个流程是用户最直接的感受,那么在技术层面有做了哪些事。OAuth授权.pngOAuth协议中的授权模式授权码模式(authorization code)简化模式(implicit)密码模式(resource owner password credentials)客户端模式(client credentials)二、SpringSecurity社交原理授权码模式流程.png // 默认拦截的请求url private static final String DEFAULT_FILTER_PROCESSES_URL = "/auth"; // 默认注册url private String signupUrl = "/signup";// 尝试认证用户private Authentication attemptAuthService(final SocialAuthenticationService<?> authService, final HttpServletRequest request, HttpServletResponse response) throws SocialAuthenticationRedirectException, AuthenticationException { final SocialAuthenticationToken token = authService.getAuthToken(request, response); if (token == null) return null; Assert.notNull(token.getConnection()); Authentication auth = getAuthentication(); if (auth == null || !auth.isAuthenticated()) { return doAuthentication(authService, request, token); } else { addConnection(authService, request, token, auth); return null; } }
2、OAuth2AuthenticationService// 用来生成授权Token(SocialAuthenticationToken)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)); } else if (StringUtils.hasText(code)) { try { // 回调地址 String returnToUrl = buildReturnToUrl(request); // 获得accessToken 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; } }
3、OAuth2Connection<A>public OAuth2Connection(String providerId, String providerUserId, String accessToken, String refreshToken, Long expireTime, OAuth2ServiceProvider<A> serviceProvider, ApiAdapter<A> apiAdapter) { super(apiAdapter); this.serviceProvider = serviceProvider; initAccessTokens(accessToken, refreshToken, expireTime); initApi(); initApiProxy(); initKey(providerId, providerUserId); }
4、AbstractConnection<A>private ServiceProviderConnectionValuesImpl setValues() { ServiceProviderConnectionValuesImpl values = new ServiceProviderConnectionValuesImpl(); apiAdapter.setConnectionValues(getApi(), values); valuesInitialized = true; ret