赞
踩
本文主要从AuthorizationEndpoint的初始化和其authorize方法等方面进行授权码获取流程分析
AuthorizationEndpoint是授权码处理端点
AuthorizationEndpoint的初始化方式在注解@EnableAuthorizationServer引入的配置类AuthorizationServerEndpointsConfiguration中。
@Configuration @Import(TokenKeyEndpointRegistrar.class) public class AuthorizationServerEndpointsConfiguration { private AuthorizationServerEndpointsConfigurer endpoints = new AuthorizationServerEndpointsConfigurer(); @Autowired private ClientDetailsService clientDetailsService; @Autowired private List<AuthorizationServerConfigurer> configurers = Collections.emptyList(); //忽略代码..... @Bean public AuthorizationEndpoint authorizationEndpoint() throws Exception { AuthorizationEndpoint authorizationEndpoint = new AuthorizationEndpoint(); FrameworkEndpointHandlerMapping mapping = getEndpointsConfigurer().getFrameworkEndpointHandlerMapping(); authorizationEndpoint.setUserApprovalPage(extractPath(mapping, "/oauth/confirm_access")); authorizationEndpoint.setProviderExceptionHandler(exceptionTranslator()); authorizationEndpoint.setErrorPage(extractPath(mapping, "/oauth/error")); authorizationEndpoint.setTokenGranter(tokenGranter()); authorizationEndpoint.setClientDetailsService(clientDetailsService); authorizationEndpoint.setAuthorizationCodeServices(authorizationCodeServices()); authorizationEndpoint.setOAuth2RequestFactory(oauth2RequestFactory()); authorizationEndpoint.setOAuth2RequestValidator(oauth2RequestValidator()); authorizationEndpoint.setUserApprovalHandler(userApprovalHandler()); authorizationEndpoint.setRedirectResolver(redirectResolver()); return authorizationEndpoint; } //忽略代码..... }
在浏览器调用如下url
http://Localhost:8080/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://www.baidu.com&scope=read
@FrameworkEndpoint @SessionAttributes({AuthorizationEndpoint.AUTHORIZATION_REQUEST_ATTR_NAME, AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME}) public class AuthorizationEndpoint extends AbstractEndpoint { @RequestMapping(value = "/oauth/authorize") public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<String, String> parameters, SessionStatus sessionStatus, Principal principal) { //首先使用OAuth2RequestFactory拉出授权请求。所有进一步的逻辑都应该是正确的 //查询授权请求,而不是返回参数映射。报告的内容 //创建参数映射后,参数映射将存储在AuthorizationRequest对象中,而不进行更改。 AuthorizationRequest authorizationRequest = getOAuth2RequestFactory().createAuthorizationRequest(parameters); //获取其response_type Set<String> responseTypes = authorizationRequest.getResponseTypes(); //response_type值必须是token或者code if (!responseTypes.contains("token") && !responseTypes.contains("code")) { throw new UnsupportedResponseTypeException("Unsupported response types: " + responseTypes); } //客户端id不能为null if (authorizationRequest.getClientId() == null) { throw new InvalidClientException("A client id must be provided"); } try { //用户第一次调这个principal肯定为null,所以他会调转到登陆页面 //用户登陆后,在次进入这个接口,principal是有值的,principal是UsernamePasswordAuthenticationToken if (!(principal instanceof Authentication) || !((Authentication) principal).isAuthenticated()) { throw new InsufficientAuthenticationException( "User must be authenticated with Spring Security before authorization can be completed."); } ClientDetails client = getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId()); //解析的重定向URI是参数中的重定向URI或参数中的重定向URI //客户详细信息。无论哪种方式,我们都需要将其存储在AuthorizationRequest上。 String redirectUriParameter = authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI); String resolvedRedirect = redirectResolver.resolveRedirect(redirectUriParameter, client); if (!StringUtils.hasText(resolvedRedirect)) { throw new RedirectMismatchException(""); } authorizationRequest.setRedirectUri(resolvedRedirect); //我们有意只验证客户机请求的参数(忽略可能存在的任何数据) //已由经理添加到请求中)。 oauth2RequestValidator.validateScope(authorizationRequest, client); //某些系统可能允许记住或默认批准批准决定。查证 //在这里输入这些逻辑,并相应地在授权请求上设置approved标志。 authorizationRequest = userApprovalHandler.checkForPreApproval(authorizationRequest,(Authentication) principal); // TODO: is this call necessary? boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal); authorizationRequest.setApproved(approved); //验证已经完成,所以我们可以检查自动批准。。。 if (authorizationRequest.isApproved()) { if (responseTypes.contains("token")) { return getImplicitGrantResponse(authorizationRequest); } if (responseTypes.contains("code")) { return new ModelAndView(getAuthorizationCodeResponse(authorizationRequest, (Authentication) principal)); } } //在会话中存储authorizationRequest和authorizationRequest的不可变映射 //将用于根据approveOrDeny()中的 model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest); model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, unmodifiableMap(authorizationRequest)); //转发到页面/oauth/confirm_access return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal); } catch (RuntimeException e) { sessionStatus.setComplete(); throw e; } } }
用户第一次调用,他会调转到登陆页面,在用户登陆后,在次进入这个接口,会转发到/oauth/confirm_access
@FrameworkEndpoint @SessionAttributes("authorizationRequest") public class WhitelabelApprovalEndpoint { @RequestMapping("/oauth/confirm_access") public ModelAndView getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception{ final String approvalContent = createTemplate(model, request); if (request.getAttribute("_csrf") != null) { model.put("_csrf", request.getAttribute("_csrf")); } View approvalView = new View() { @Override public String getContentType() { return "text/html"; } @Override public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { response.setContentType(getContentType()); response.getWriter().append(approvalContent); } }; return new ModelAndView(approvalView, model); } protected String createTemplate(Map<String, Object> model, HttpServletRequest request) { AuthorizationRequest authorizationRequest = (AuthorizationRequest) model.get("authorizationRequest"); String clientId = authorizationRequest.getClientId(); StringBuilder builder = new StringBuilder(); builder.append("<html><body><h1>OAuth Approval</h1>"); builder.append("<p>Do you authorize \"").append(HtmlUtils.htmlEscape(clientId)); builder.append("\" to access your protected resources?</p>"); builder.append("<form id=\"confirmationForm\" name=\"confirmationForm\" action=\""); String requestPath = ServletUriComponentsBuilder.fromContextPath(request).build().getPath(); if (requestPath == null) { requestPath = ""; } builder.append(requestPath).append("/oauth/authorize\" method=\"post\">"); builder.append("<input name=\"user_oauth_approval\" value=\"true\" type=\"hidden\"/>"); String csrfTemplate = null; CsrfToken csrfToken = (CsrfToken) (model.containsKey("_csrf") ? model.get("_csrf") : request.getAttribute("_csrf")); if (csrfToken != null) { csrfTemplate = "<input type=\"hidden\" name=\"" + HtmlUtils.htmlEscape(csrfToken.getParameterName()) + "\" value=\"" + HtmlUtils.htmlEscape(csrfToken.getToken()) + "\" />"; } if (csrfTemplate != null) { builder.append(csrfTemplate); } String authorizeInputTemplate = "<label><input name=\"authorize\" value=\"Authorize\" type=\"submit\"/></label></form>"; if (model.containsKey("scopes") || request.getAttribute("scopes") != null) { builder.append(createScopes(model, request)); builder.append(authorizeInputTemplate); } else { builder.append(authorizeInputTemplate); builder.append("<form id=\"denialForm\" name=\"denialForm\" action=\""); builder.append(requestPath).append("/oauth/authorize\" method=\"post\">"); builder.append("<input name=\"user_oauth_approval\" value=\"false\" type=\"hidden\"/>"); if (csrfTemplate != null) { builder.append(csrfTemplate); } builder.append("<label><input name=\"deny\" value=\"Deny\" type=\"submit\"/></label></form>"); } builder.append("</body></html>"); return builder.toString(); } private CharSequence createScopes(Map<String, Object> model, HttpServletRequest request) { StringBuilder builder = new StringBuilder("<ul>"); @SuppressWarnings("unchecked") Map<String, String> scopes = (Map<String, String>) (model.containsKey("scopes") ? model.get("scopes") : request.getAttribute("scopes")); for (String scope : scopes.keySet()) { String approved = "true".equals(scopes.get(scope)) ? " checked" : ""; String denied = !"true".equals(scopes.get(scope)) ? " checked" : ""; scope = HtmlUtils.htmlEscape(scope); builder.append("<li><div class=\"form-group\">"); builder.append(scope).append(": <input type=\"radio\" name=\""); builder.append(scope).append("\" value=\"true\"").append(approved).append(">Approve</input> "); builder.append("<input type=\"radio\" name=\"").append(scope).append("\" value=\"false\""); builder.append(denied).append(">Deny</input></div></li>"); } builder.append("</ul>"); return builder.toString(); } }
点击Approve成功后调url:/oauth/authorize,他是一个post接口
@FrameworkEndpoint @SessionAttributes({AuthorizationEndpoint.AUTHORIZATION_REQUEST_ATTR_NAME, AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME}) public class AuthorizationEndpoint extends AbstractEndpoint { @RequestMapping(value = "/oauth/authorize", method = RequestMethod.POST, params = OAuth2Utils.USER_OAUTH_APPROVAL) public View approveOrDeny(@RequestParam Map<String, String> approvalParameters, Map<String, ?> model, SessionStatus sessionStatus, Principal principal) { if (!(principal instanceof Authentication)) { sessionStatus.setComplete(); throw new InsufficientAuthenticationException(""); } AuthorizationRequest authorizationRequest = (AuthorizationRequest) model.get(AUTHORIZATION_REQUEST_ATTR_NAME); if (authorizationRequest == null) { sessionStatus.setComplete(); throw new InvalidRequestException(""); } //检查以确保在用户批准步骤期间未修改授权请求 @SuppressWarnings("unchecked") Map<String, Object> originalAuthorizationRequest = (Map<String, Object>) model.get(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME); if (isAuthorizationRequestModified(authorizationRequest, originalAuthorizationRequest)) { throw new InvalidRequestException("."); } try { Set<String> responseTypes = authorizationRequest.getResponseTypes(); authorizationRequest.setApprovalParameters(approvalParameters); authorizationRequest = userApprovalHandler.updateAfterApproval(authorizationRequest, (Authentication) principal); boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal); authorizationRequest.setApproved(approved); if (authorizationRequest.getRedirectUri() == null) { sessionStatus.setComplete(); throw new InvalidRequestException("Cannot approve request when no redirect URI is provided."); } if (!authorizationRequest.isApproved()) { return new RedirectView(getUnsuccessfulRedirect(authorizationRequest, new UserDeniedAuthorizationException("User denied access"), responseTypes.contains("token")), false, true, false); } if (responseTypes.contains("token")) { return getImplicitGrantResponse(authorizationRequest).getView(); } return getAuthorizationCodeResponse(authorizationRequest, (Authentication) principal); } finally { sessionStatus.setComplete(); } } private View getAuthorizationCodeResponse(AuthorizationRequest authorizationRequest, Authentication authUser) { try { return new RedirectView(getSuccessfulRedirect(authorizationRequest, generateCode(authorizationRequest, authUser)), false, true, false); } catch (OAuth2Exception e) { return new RedirectView(getUnsuccessfulRedirect(authorizationRequest, e, false), false, true, false); } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。