zoukankan      html  css  js  c++  java
  • Spring Security OAuth2 授权码模式

     背景:

        由于业务实现中涉及到接入第三方系统(app接入有赞商城等),所以涉及到第三方系统需要获取用户信息(用户手机号、姓名等),为了保证用户信息的安全和接入方式的统一,

    采用Oauth2四种模式之一的授权码模式。

     介绍:

          

    • 第三方系统调用我方提供的授权接口(步骤1)
    • 用户同意授权,后跳转第三方系统(步骤2、3)
    • 第三方系统获得code,根据code到我方系统获取token(步骤5、6 )
    • 根据获取token访问受保护的资源(步骤8、9)

         实际应用中由于合作商户,所以需要直接返回code,不需要用户手动授权,即静默模式,所以需要扩展框架,使其支持自动授权

    扩展:

         项目使用的是spring-security-oauth2-2.0.15 由于默认情况下,需要用户授权通过才能生成授权码。所以需简要对框架进行扩展

    (1)spring-security-oauth2获取code的controller:

     1 RequestMapping(value = "/oauth/authorize")
     2     public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<String, String> parameters,
     3             SessionStatus sessionStatus, Principal principal) {
     4 
     5         // Pull out the authorization request first, using the OAuth2RequestFactory. All further logic should
     6         // query off of the authorization request instead of referring back to the parameters map. The contents of the
     7         // parameters map will be stored without change in the AuthorizationRequest object once it is created.
     8         AuthorizationRequest authorizationRequest = getOAuth2RequestFactory().createAuthorizationRequest(parameters);
     9 
    10         Set<String> responseTypes = authorizationRequest.getResponseTypes();
    11 
    12         if (!responseTypes.contains("token") && !responseTypes.contains("code")) {
    13             throw new UnsupportedResponseTypeException("Unsupported response types: " + responseTypes);
    14         }
    15 
    16         if (authorizationRequest.getClientId() == null) {
    17             throw new InvalidClientException("A client id must be provided");
    18         }
    19 
    20         try {
    21 
    22             if (!(principal instanceof Authentication) || !((Authentication) principal).isAuthenticated()) {
    23                 throw new InsufficientAuthenticationException(
    24                         "User must be authenticated with Spring Security before authorization can be completed.");
    25             }
    26 
    27             ClientDetails client = getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId());
    28 
    29             // The resolved redirect URI is either the redirect_uri from the parameters or the one from
    30             // clientDetails. Either way we need to store it on the AuthorizationRequest.
    31             String redirectUriParameter = authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI);
    32             String resolvedRedirect = redirectResolver.resolveRedirect(redirectUriParameter, client);
    33             if (!StringUtils.hasText(resolvedRedirect)) {
    34                 throw new RedirectMismatchException(
    35                         "A redirectUri must be either supplied or preconfigured in the ClientDetails");
    36             }
    37             authorizationRequest.setRedirectUri(resolvedRedirect);
    38 
    39             // We intentionally only validate the parameters requested by the client (ignoring any data that may have
    40             // been added to the request by the manager).
    41             oauth2RequestValidator.validateScope(authorizationRequest, client);
    42 
    43             // Some systems may allow for approval decisions to be remembered or approved by default. Check for
    44             // such logic here, and set the approved flag on the authorization request accordingly.
    45             authorizationRequest = userApprovalHandler.checkForPreApproval(authorizationRequest,
    46                     (Authentication) principal);
    47             // TODO: is this call necessary?
    48             boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal);
    49             authorizationRequest.setApproved(approved);
    50 
    51             // Validation is all done, so we can check for auto approval...
    52             if (authorizationRequest.isApproved()) {
    53                 if (responseTypes.contains("token")) {
    54                     return getImplicitGrantResponse(authorizationRequest);
    55                 }
    56                 if (responseTypes.contains("code")) {
    57                     return new ModelAndView(getAuthorizationCodeResponse(authorizationRequest,
    58                             (Authentication) principal));
    59                 }
    60             }
    61 
    62             // Place auth request into the model so that it is stored in the session
    63             // for approveOrDeny to use. That way we make sure that auth request comes from the session,
    64             // so any auth request parameters passed to approveOrDeny will be ignored and retrieved from the session.
    65             model.put("authorizationRequest", authorizationRequest);
    66 
    67             return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal);
    68 
    69         }
    70         catch (RuntimeException e) {
    71             sessionStatus.setComplete();
    72             throw e;
    73         }
    74 
    75     }

    52行到59行可知, 当approved 为true的时候会直接返回code码,不会需要用户授权,所以问题变成了如何让扩展的userApprovalHandler生效

    (2)扩展的userApprovalHandler生效

     1   @Override
     2   public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
     3     endpoints
     4         .tokenStore(tokenStore)
     5         .authenticationManager(authenticationManager)
     6         .userDetailsService(authUserDetailService)
     7         .authorizationCodeServices(new JdbcAuthorizationCodeServices(dataSource))
     8         .reuseRefreshTokens(false)
     9         .userApprovalHandler(new AuthApprovalHandler())
    10         .exceptionTranslator(customWebResponseExceptionTranslator)
    11     ;
    12   }

       9行:AuthotizationServer中增加配置自定义配置

    应用:

    (1)获取code值

       需要APP端定制webview开发,根据/oauth/authrorize路径参数中增加token

      请求路径: 

      参数说明:

    参数名称
    类型
    是否必填
    描述
    response_type
    String 固定值“code”
    client_id
    String 第三方配置的client_id
    redirect_uri
    String 第三方配置的回调地址
         state String 第三方自定义使用


      请求示例:

    curl -X POST http://localhost:8421/oauth/authorize -H 'Authorization: Bearer b7c2d63f-edff-4790-add9-0b69df7321b5' -d 'response_type=code&client_id=external&redirect_uri=http://www.baidu.com&state=123'

       返回结果:  重定向redirect_uri路径

      (2)获取accessToken(有效期暂定72h

      请求参数:

    参数名称
    类型
    是否必填
    描述
    client_id
    String 第三方配置的client_id
    client_secret
    String 第三方配置的密钥
        code String 申请的code
    grant_type
    String 固定值“authorization_code”
    redirect_uri
    String 第三方配置的回调地址,必须与生成code时的uri一样

      请求示例:

    curl -X POST http://localhost:8421/oauth/token -d 'grant_type=authorization_code&client_id=external&client_secret=D524C1A0811DA49592F841085CC0063EB62B3001252A94542795D1CA9824A941&redirect_uri=http://www.baidu.com&code=4TCYkV'

      返回结果:

    {"access_token":"95b5be18-49a3-44e1-a527-d5da036cfc3f","token_type":"bearer","refresh_token":"b4488c7d-1e8c-4317-a955-1f4bda013a35","expires_in":9891370,"scope":"auth_base"}

      (3) 获取refreshToken

          暂时不支持

      (4) 访问资源(用户信息)

          根据获取到的授权token访问用户资源信息

         请求示例:

    curl -X POST https://wuxi.test.brightcns.cn/api/v2/user/external/info -H 'Authorization: Bearer e86d752e-8d72-4a33-aa98-8e158ac5b50b'

         返回结果:

    {"success":true,"msg":"success","code":"SUCCESS","data":{"userId":4738295200051366773,"phone":13916413714,"nickname":"13916413714","avatar":"http://wxcardoss.oss-cn-shanghai.aliyuncs.com/null","realName":null,"extendParam":null}}

     

        

     

        

  • 相关阅读:
    HDU 3586 二分答案+树形DP判定
    POJ 3140 树形DP
    POJ 1741 树的点分治
    POJ 1655 求树的重心
    CF 219D 树形DP
    HDU 2196树形DP(2个方向)
    HDU 1520 树形DP入门
    POJ 1159 Palindrome(最长公共子序列)
    树状数组 区间更新 区间查询
    HDU 1556 BIT区间修改+单点查询(fread读入优化)
  • 原文地址:https://www.cnblogs.com/mxmbk/p/9882860.html
Copyright © 2011-2022 走看看