• Spring Security OAuth2 token权限隔离


       由于项目OAuth2采用了多种模式,授权码模式为第三方系统接入,密码模式用于用户登录,Client模式用于服务间调用,

    所有不同的模式下的token需要用  @PreAuthorize("hasAuthority('client')") 进行隔离,遇到问题一直验证不通过。

      通过调试发现资源服务从授权服务拿到的authrities字段一直为空, StackOverFlow说低版本(项目中才2.0.15)的OAuth2实现权限隔离需要 重写UserInfoTokenService

      但是资源服务太多所以考虑重写授权服务的返回值,如何重写?在哪里重写?是下面要介绍的~

    一、哪里重写?

           资源服务器向授权服务服务器获取资源时候,返回的user信息重写,加入authorities

    @RestController
    @Slf4j
    public class UserController {
    
      @Autowired
      HttpServletRequest request;
    
      @GetMapping("/user")
      public Principal user(Principal principal) {
        log.info("获取user信息:{}", JSON.toJSON(principal));
    return principal;
    }

        返回的具体用户信息:

      1 {
      2     "principal": {
      3         "password": "$2a$10$OjTFAZEzS6qypY4nRZtnM.MzS6F3XsIlkAO/kIFCu30kAk8Yasowa",
      4         "phone": "13918438965",
      5         "credentialsNonExpired": true,
      6         "accountNonExpired": true,
      7         "enabled": true,
      8         "accountNonLocked": true,
      9         "username": "4738195728608789333"
     10     },
     11     "authenticated": true,
     12     "oAuth2Request": {
     13         "redirectUri": "http://www.baidu.com",
     14         "responseTypes": ["code"],
     15         "approved": true,
     16         "extensions": {},
     17         "clientId": "external",
     18         "scope": ["auth_base"],
     19         "requestParameters": {
     20             "code": "ovzMSk",
     21             "grant_type": "authorization_code",
     22             "scope": "auth_base",
     23             "response_type": "code",
     24             "redirect_uri": "http://www.baidu.com",
     25             "state": "123",
     26             "client_secret": "D524C1A0811DA49592F841085CC0063EB62B3001252A9454",
     27             "client_id": "external"
     28         },
     29         "refresh": false,
     30         "grantType": "authorization_code",
     31         "authorities": [{
     32             "authority": "auth_base"
     33         }],
     34         "resourceIds": []
     35     },
     36     "clientOnly": false,
     37     "credentials": "",
     38     "name": "4738195728608789333",
     39     "userAuthentication": {
     40         "principal": {
     41             "password": "$2a$10$OjTFAZEzS6qypY4nRZtnM.MzS6F3XsIlkAO/kIFCu30kAk8Yasowa",
     42             "phone": "13918438965",
     43             "credentialsNonExpired": true,
     44             "accountNonExpired": true,
     45             "enabled": true,
     46             "accountNonLocked": true,
     47             "username": "4738195728608789333"
     48         },
     49         "authenticated": true,
     50         "oAuth2Request": {
     51             "responseTypes": [],
     52             "approved": true,
     53             "extensions": {},
     54             "clientId": "gt",
     55             "scope": ["frontend"],
     56             "requestParameters": {
     57                 "auth_type": "sms",
     58                 "device_id": "5c5d1d7b-50ae-4347-9aee-7a7686055f4d",
     59                 "grant_type": "password",
     60                 "client_id": "gt",
     61                 "username": "13918438965"
     62             },
     63             "refresh": false,
     64             "grantType": "password",
     65             "authorities": [{
     66                 "authority": "client"
     67             }],
     68             "resourceIds": []
     69         },
     70         "clientOnly": false,
     71         "credentials": "",
     72         "name": "4738195728608789333",
     73         "userAuthentication": {
     74             "principal": {
     75                 "password": "$2a$10$OjTFAZEzS6qypY4nRZtnM.MzS6F3XsIlkAO/kIFCu30kAk8Yasowa",
     76                 "phone": "13918438965",
     77                 "credentialsNonExpired": true,
     78                 "accountNonExpired": true,
     79                 "enabled": true,
     80                 "accountNonLocked": true,
     81                 "username": "4738195728608789333"
     82             },
     83             "authenticated": true,
     84             "name": "4738195728608789333",
     85             "details": {
     86                 "auth_type": "sms",
     87                 "device_id": "5c5d1d7b-50ae-4347-9aee-7a7686055f4d",
     88                 "grant_type": "password",
     89                 "client_secret": "D524C1A0811DA49592F841085CC0063EB62B3001252A94542795D1CA9824A941",
     90                 "client_id": "gt",
     91                 "username": "13918438965"
     92             },
     93             "authorities": []
     94         },
     95         "details": {
     96             "tokenType": "Bearer",
     97             "tokenValue": "f7870e71-7b0f-4a4a-9c6f-bb6d1f903ad9",
     98             "remoteAddress": "0:0:0:0:0:0:0:1"
     99         },
    100         "authorities": []
    101     },
    102     "details": {
    103         "tokenType": "Bearer",
    104         "tokenValue": "7829005c-5ebe-4428-b951-89477b24316e",
    105         "remoteAddress": "0:0:0:0:0:0:0:1"
    106     },
    107     "authorities": []
    108 }
    View Code

    二、如何重写?

    principal是OAuth2Authentication实例,OAuth2Authentication主要包括OAuth2Request storedRequest、Authentication userAuthentication,
    重写目的是将
    storedRequest authorities复制到authoritie中,但问题是authoritie不让修改的,没办法只能重写这个OAuth2Authentication了。

    为了改变authoritie重写:
    @GetMapping("/user")
      public Principal user(Principal principal) {
        log.info("获取user信息:{}", JSON.toJSON(principal));
        OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) principal;
        OAuth2Request storedRequest = oAuth2Authentication.getOAuth2Request();
        Authentication userAuthentication = oAuth2Authentication.getUserAuthentication();
        // 为了服务端进行token权限隔离 定制OAuth2Authentication
        CustomOAuth2Authentication customOAuth2Authentication = new CustomOAuth2Authentication(storedRequest, userAuthentication, storedRequest.getAuthorities());
        customOAuth2Authentication.setDetails(oAuth2Authentication.getDetails());
        log.info("返回用户信息:{}", JSON.toJSON(customOAuth2Authentication));
        return customOAuth2Authentication;
      }

      CustomOAuth2Authentication :

      1 package com.brightcns.wuxi.citizencard.auth.domain;
      2 
      3 import org.springframework.security.authentication.AbstractAuthenticationToken;
      4 import org.springframework.security.core.Authentication;
      5 import org.springframework.security.core.CredentialsContainer;
      6 import org.springframework.security.core.GrantedAuthority;
      7 import org.springframework.security.oauth2.provider.OAuth2Request;
      8 
      9 import java.util.Collection;
     10 
     11 /**
     12  * @author maxianming
     13  * @date 2018/10/29 13:53
     14  */
     15 public class CustomOAuth2Authentication extends AbstractAuthenticationToken {
     16 
     17     private static final long serialVersionUID = -4809832298438307309L;
     18 
     19     private final OAuth2Request storedRequest;
     20 
     21     private final Authentication userAuthentication;
     22 
     23     /**
     24      * Construct an OAuth 2 authentication. Since some grant types don't require user authentication, the user
     25      * authentication may be null.
     26      * @param storedRequest      The authorization request (must not be null).
     27      * @param userAuthentication The user authentication (possibly null).
     28      */
     29     public CustomOAuth2Authentication(OAuth2Request storedRequest, Authentication userAuthentication, Collection<? extends GrantedAuthority> authorities) {
     30         /**
     31          * 为了服务端进行token权限隔离 {@link @PreAuthorize("hasAuthority('server')")},自定义OAuth2Authentication使得支持改变authorities
     32          */
     33         super(authorities != null ? authorities : userAuthentication == null ? storedRequest.getAuthorities() : userAuthentication.getAuthorities());
     34         this.storedRequest = storedRequest;
     35         this.userAuthentication = userAuthentication;
     36     }
     37 
     38     public Object getCredentials() {
     39         return "";
     40     }
     41 
     42     public Object getPrincipal() {
     43         return this.userAuthentication == null ? this.storedRequest.getClientId() : this.userAuthentication
     44                 .getPrincipal();
     45     }
     46 
     47     /**
     48      * Convenience method to check if there is a user associated with this token, or just a client application.
     49      *
     50      * @return true if this token represents a client app not acting on behalf of a user
     51      */
     52     public boolean isClientOnly() {
     53         return userAuthentication == null;
     54     }
     55 
     56     /**
     57      * The authorization request containing details of the client application.
     58      *
     59      * @return The client authentication.
     60      */
     61     public OAuth2Request getOAuth2Request() {
     62         return storedRequest;
     63     }
     64 
     65     /**
     66      * The user authentication.
     67      *
     68      * @return The user authentication.
     69      */
     70     public Authentication getUserAuthentication() {
     71         return userAuthentication;
     72     }
     73 
     74     @Override
     75     public boolean isAuthenticated() {
     76         return this.storedRequest.isApproved()
     77                 && (this.userAuthentication == null || this.userAuthentication.isAuthenticated());
     78     }
     79 
     80     @Override
     81     public void eraseCredentials() {
     82         super.eraseCredentials();
     83         if (this.userAuthentication != null && CredentialsContainer.class.isAssignableFrom(this.userAuthentication.getClass())) {
     84             CredentialsContainer.class.cast(this.userAuthentication).eraseCredentials();
     85         }
     86     }
     87 
     88     @Override
     89     public boolean equals(Object o) {
     90         if (this == o) {
     91             return true;
     92         }
     93         if (!(o instanceof CustomOAuth2Authentication)) {
     94             return false;
     95         }
     96         if (!super.equals(o)) {
     97             return false;
     98         }
     99 
    100         CustomOAuth2Authentication that = (CustomOAuth2Authentication) o;
    101 
    102         if (!storedRequest.equals(that.storedRequest)) {
    103             return false;
    104         }
    105         if (userAuthentication != null ? !userAuthentication.equals(that.userAuthentication)
    106                 : that.userAuthentication != null) {
    107             return false;
    108         }
    109 
    110         if (getDetails() != null ? !getDetails().equals(that.getDetails()) : that.getDetails() != null) {
    111             // return false;
    112         }
    113 
    114         return true;
    115     }
    116 
    117     @Override
    118     public int hashCode() {
    119         int result = super.hashCode();
    120         result = 31 * result + storedRequest.hashCode();
    121         result = 31 * result + (userAuthentication != null ? userAuthentication.hashCode() : 0);
    122         return result;
    123     }
    124 
    125 }

    主要在OAuth2Authentication基础上修改了30-35行代码



      

  • 相关阅读:
    数据分析公司—新材料行业报告资源网站
    Mac——四指手势、三指手势、二指手势与快捷键
    Mac——效率工具之CatchMouse,在多个硬件屏幕之间设置快捷键切换
    TGI指标的意思是什么
    时事政治
    Linux——history命令
    Mac——Mac手势快捷键—四指操作技巧详解
    站点的SEO优化
    Aerospike数据库基本概念及与Redis缓存数据库的关系
    Chrome关闭侧边栏
  • 原文地址:https://www.cnblogs.com/mxmbk/p/9883945.html
Copyright © 2020-2023  润新知