@FrameworkEndpoint
@SessionAttributes("authorizationRequest")
public class AuthorizationEndpoint extends AbstractEndpoint {
//omitted...
@RequestMapping(value = "/oauth/authorize")
public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<String, String> parameters,
SessionStatus sessionStatus, Principal principal) {
// Pull out the authorization request first, using the OAuth2RequestFactory. All further logic should
// query off of the authorization request instead of referring back to the parameters map. The contents of the
// parameters map will be stored without change in the AuthorizationRequest object once it is created.
// 通过Oauth2RequestFactory构建AuthorizationRequest
AuthorizationRequest authorizationRequest = getOAuth2RequestFactory().createAuthorizationRequest(parameters);
Set<String> responseTypes = authorizationRequest.getResponseTypes();
///oauth/authorize这个请求只支持授权码code模式和Implicit隐式模式
if (!responseTypes.contains("token") && !responseTypes.contains("code")) {
throw new UnsupportedResponseTypeException("Unsupported response types: " + responseTypes);
}
if (authorizationRequest.getClientId() == null) {
throw new InvalidClientException("A client id must be provided");
}
try {
//Oauth2授权的第一步就是要确保用户是否已经登陆,然后才会
//这里体现的是SecurityContext中是否包涵了已经授权的Authentication身份
if (!(principal instanceof Authentication) || !((Authentication) principal).isAuthenticated()) {
throw new InsufficientAuthenticationException(
"User must be authenticated with Spring Security before authorization can be completed.");
}
//通过ClientDetailsService检索ClientDetails
ClientDetails client = getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId());
// The resolved redirect URI is either the redirect_uri from the parameters or the one from
// clientDetails. Either way we need to store it on the AuthorizationRequest.
String redirectUriParameter = authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI);
String resolvedRedirect = redirectResolver.resolveRedirect(redirectUriParameter, client);
//确保requst中有重定向redirect_uri
if (!StringUtils.hasText(resolvedRedirect)) {
throw new RedirectMismatchException(
"A redirectUri must be either supplied or preconfigured in the ClientDetails");
}
authorizationRequest.setRedirectUri(resolvedRedirect);
// We intentionally only validate the parameters requested by the client (ignoring any data that may have
// been added to the request by the manager).
// 校验client请求的是一组有效的scope,通过比对表oauth_client_details
oauth2RequestValidator.validateScope(authorizationRequest, client);
// Some systems may allow for approval decisions to be remembered or approved by default. Check for
// such logic here, and set the approved flag on the authorization request accordingly.
//预同意处理(ApprovalStoreUserApprovalHandler)
//1. 校验所有的scope是否已经全部是自动同意授权,如果全部自动授权同意,则设置authorizationRequest
//中属性approved为true,否则走2
//2. 查询client_id下所有oauth_approvals,校验在有效时间内Scope授权的情况,如果在有效时间内Scope授权全部同意,
//则设置authorizationRequest中属性approved为true,否则为false
authorizationRequest = userApprovalHandler.checkForPreApproval(authorizationRequest,
(Authentication) principal);
// TODO: is this call necessary?
// 这个步骤是不是多余的??
boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal);
authorizationRequest.setApproved(approved);
// Validation is all done, so we can check for auto approval...
// 如果预授权结果是同意,直接将code重定向到redirect_uri
if (authorizationRequest.isApproved()) {
if (responseTypes.contains("token")) {
return getImplicitGrantResponse(authorizationRequest);
}
if (responseTypes.contains("code")) {
return new ModelAndView(getAuthorizationCodeResponse(authorizationRequest,
(Authentication) principal));
}
}
// Place auth request into the model so that it is stored in the session
// for approveOrDeny to use. That way we make sure that auth request comes from the session,
// so any auth request parameters passed to approveOrDeny will be ignored and retrieved from the session.
model.put("authorizationRequest", authorizationRequest);
//否则跳转到授权页面
//授权页面是由WhitelabelApprovalEndpoint类生成的
return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal);
}
catch (RuntimeException e) {
sessionStatus.setComplete();
throw e;
}
}
//omitted...
}
AuthorizationEndpoint中采用两种常用的UserApprovalHandler策略,一种是细精粒度的基于Scope的ApprovalStoreUserApprovalHandler,一种是粗粒度的基于token的TokenStoreUserApprovalHandler。两种策略不同之处就是在授权页面上,基于Scope的ApprovalStoreUserApprovalHandler策略需要为每一个Scope授权同意,而粗粒度的基于token的TokenStoreUserApprovalHandler因为是基于token的,所以体现在页面上的只有一个同意或者拒绝的按钮来表示是否对用户的操作进行授权