【问题标题】:How to custom Principle object with Spring Cloud Security?如何使用 Spring Cloud Security 自定义 Principle 对象?
【发布时间】:2015-11-07 03:29:23
【问题描述】:

我终于让我的 oauth2 服务器运行起来了。

从命令行,如果我运行

curl -s -u acme:acmesecret -d grant_type=password -d username=myusername -d password=mypassword -H Accept:application/json http://localhost:9999/oauth/token 

我得到了下面的结果,

{
    "access_token":"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0Mzk1NDU3ODAsInVzZXJfbmFtZSI6IisxIDQwODUxODIxMTUiLCJhdXRob3JpdGllcyI6WyJVU0VSIiwiQURNSU4iXSwianRpIjoiYmFkMDgyMjctNDExNC00OTZkLWE1NDMtYzBhMjc3YTBhZDkzIiwiY2xpZW50X2lkIjoiYWNtZSIsInNjb3BlIjpbIndlYnNob3AiXX0.CM_0gBHVyecOMmpc2cnKTus48PNv8gfHDyzVOVa5TBDxv4QlnDO93otmUs86IQqPaqaI133tT1NPU0pt2dbV5lrY3FOlPFXB0zZw5ptIXCtpaQLgl3e9hkB1aSfv3YxbHiOV8n3FcvNdz9Ihi9XEQdzqT8YfK7mCeMOjdb1i6Ve9axwjJI9ZHxXzDMcJsnYBcQCKG52G3-rWzgzlaQkPZY6mO7q0eO0jgVWthLfSBumHlDt9QXaBkETH3CRHxSuJqlo4J3TZxP4-1vPLkgh8Ku2rY5A9rT-xOKG8_5s2CJduCZt0qQrXZhz7sk0m2IdxDDwXumPv6zyHyD2J3sjHUA",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJSUzI1NiJ9.eyJ1c2VyX25hbWUiOiIrMSA0MDg1MTgyMTE1Iiwic2NvcGUiOlsid2Vic2hvcCJdLCJhdGkiOiJiYWQwODIyNy00MTE0LTQ5NmQtYTU0My1jMGEyNzdhMGFkOTMiLCJleHAiOjE0NDIwOTQ1ODAsImF1dGhvcml0aWVzIjpbIlVTRVIiLCJBRE1JTiJdLCJqdGkiOiJjYWNkOWEzOC1mOWE5LTQ4NjAtOWZmMi05NWMzMzU4MmY0NDAiLCJjbGllbnRfaWQiOiJhY21lIn0.DhaqIEdYWR2VPkgh72bQ17ZLqcVVfdYtT8DdKibjIcZUTNNjN_atdyKYKNEtdSyEES-ArHL0jCVXUg3EKiut_qtvn8oaLYEAxCNfztHyo_b-RZIxOgr71m82n66vSwRzxQnoKcGltxpZs-PK5p-gmbaEWK4EO63AkJpgN_IrIGV4eVQmidanz53rvq-CBiq-1FFb64OilesUxkSPOVkbb-q-mUmd8EG4khdbf44LD9VhyZwt8lOOi8NnksnnGhogiynU9p7tirAv6w_g8IO7uy06fWaLyn6rAgPga3CYgo9ggFIICWKn-QFipkHgiehq6y_1-xTGlgHnRKXcnPIZcg",
    "expires_in": 34996,
    "scope": "webshop",
    "jti": "bad08227-4114-496d-a543-c0a277a0ad93"
}

返回令牌后,我可以使用 curl 命令获取用户信息。您可以在响应中包含大量用户信息。

curl http://localhost:9999/user -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0Mzk1NDU3ODAsInVzZXJfbmFtZSI6IisxIDQwODUxODIxMTUiLCJhdXRob3JpdGllcyI6WyJVU0VSIiwiQURNSU4iXSwianRpIjoiYmFkMDgyMjctNDExNC00OTZkLWE1NDMtYzBhMjc3YTBhZDkzIiwiY2xpZW50X2lkIjoiYWNtZSIsInNjb3BlIjpbIndlYnNob3AiXX0.CM_0gBHVyecOMmpc2cnKTus48PNv8gfHDyzVOVa5TBDxv4QlnDO93otmUs86IQqPaqaI133tT1NPU0pt2dbV5lrY3FOlPFXB0zZw5ptIXCtpaQLgl3e9hkB1aSfv3YxbHiOV8n3FcvNdz9Ihi9XEQdzqT8YfK7mCeMOjdb1i6Ve9axwjJI9ZHxXzDMcJsnYBcQCKG52G3-rWzgzlaQkPZY6mO7q0eO0jgVWthLfSBumHlDt9QXaBkETH3CRHxSuJqlo4J3TZxP4-1vPLkgh8Ku2rY5A9rT-xOKG8_5s2CJduCZt0qQrXZhz7sk0m2IdxDDwXumPv6zyHyD2J3sjHUA" 

{
  "details": {
    "remoteAddress": "127.0.0.1",
    "sessionId": null,
    "tokenValue": "eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0Mzk1NDU3ODAsInVzZXJfbmFtZSI6IisxIDQwODUxODIxMTUiLCJhdXRob3JpdGllcyI6WyJVU0VSIiwiQURNSU4iXSwianRpIjoiYmFkMDgyMjctNDExNC00OTZkLWE1NDMtYzBhMjc3YTBhZDkzIiwiY2xpZW50X2lkIjoiYWNtZSIsInNjb3BlIjpbIndlYnNob3AiXX0.CM_0gBHVyecOMmpc2cnKTus48PNv8gfHDyzVOVa5TBDxv4QlnDO93otmUs86IQqPaqaI133tT1NPU0pt2dbV5lrY3FOlPFXB0zZw5ptIXCtpaQLgl3e9hkB1aSfv3YxbHiOV8n3FcvNdz9Ihi9XEQdzqT8YfK7mCeMOjdb1i6Ve9axwjJI9ZHxXzDMcJsnYBcQCKG52G3-rWzgzlaQkPZY6mO7q0eO0jgVWthLfSBumHlDt9QXaBkETH3CRHxSuJqlo4J3TZxP4-1vPLkgh8Ku2rY5A9rT-xOKG8_5s2CJduCZt0qQrXZhz7sk0m2IdxDDwXumPv6zyHyD2J3sjHUA",
    "tokenType": "Bearer",
    "decodedDetails": null
  },
  "authorities": [
    {
      "authority": "USER"
    },
    {
      "authority": "ADMIN"
    }
  ],
  "authenticated": true,
  "userAuthentication": {
    "details": {
      "grant_type": "password",
      "username": "myusername"
    },
    "authorities": [
      {
        "authority": "USER"
      },
      {
        "authority": "ADMIN"
      }
    ],
    "authenticated": true,
    "principal": {
      "id": "usr000d11b4c86-13ba-11e5-b905-56847afe9799",
      "json": null,
      "version": 0,
      "created": 1434412879774,
      "updated": 1438877901186,
      "info": {
        "nickName": "Kevin",
        "country": "China",
        "zipcode": null,
        "state": null,
        "city": "",
        "occupation": "",
        "gender": null,
        "imgPath": "https://ddbs0erhouflt.cloudfront.net/mcf000ecd36bcb-f33e-4d50-9102-7a8706b45eb8",
        "about": "",
        "dueDate": 1447312895201,
        "birthday": 0
      },
      "privateInfo": {
        "email": "zyj@yahoo.com",
        "phone": "myusername",
        "password": "f45206ce4247b5d9af350d4600adc85c",
        "tempPassword": null,
        "tokens": null
      },
      "settings": null,
      "type": "Super",
      "status": "Active",
      "enabled": true,
      "username": "myusername",
      "password": "f45206ce4247b5d9af350d4600adc85c",
      "accountNonExpired": true,
      "accountNonLocked": true,
      "credentialsNonExpired": true,
      "authorities": [
        {
          "authority": "USER"
        },
        {
          "authority": "ADMIN"
        }
      ]
    },
    "credentials": null,
    "name": "myusername"
  },
  "credentials": "",
  "oauth2Request": {
    "clientId": "acme",
    "scope": [
      "webshop"
    ],
    "requestParameters": {
      "grant_type": "password",
      "username": "myusername"
    },
    "resourceIds": [],
    "authorities": [],
    "approved": true,
    "refresh": false,
    "redirectUri": null,
    "responseTypes": [],
    "extensions": {},
    "grantType": "password",
    "refreshTokenRequest": null
  },
  "principal": {
    "id": "usr000d11b4c86-13ba-11e5-b905-56847afe9799",
    "json": null,
    "version": 0,
    "created": 1434412879774,
    "updated": 1438877901186,
    "info": {
      "nickName": "Kevin",
      "country": "China",
      "zipcode": null,
      "state": null,
      "city": "",
      "occupation": "",
      "gender": null,
      "imgPath": "https://ddbs0erhouflt.cloudfront.net/mcf000ecd36bcb-f33e-4d50-9102-7a8706b45eb8",
      "about": "",
      "dueDate": 1447312895201,
      "birthday": 0
    },
    "privateInfo": {
      "email": "zyj@yahoo.com",
      "phone": "myusername",
      "password": "f45206ce4247b5d9af350d4600adc85c",
      "tempPassword": null,
      "tokens": null
    },
    "settings": null,
    "type": "Super",
    "status": "Active",
    "enabled": true,
    "username": "myusername",
    "password": "f45206ce4247b5d9af350d4600adc85c",
    "accountNonExpired": true,
    "accountNonLocked": true,
    "credentialsNonExpired": true,
    "authorities": [
      {
        "authority": "USER"
      },
      {
        "authority": "ADMIN"
      }
    ]
  },
  "clientOnly": false,
  "name": "myusername"
}

我有一个 Spring Boot 微服务客户端。它使用spring-cloud-security。以下是其中一项网络服务,

@RequestMapping(value="getsth", method=RequestMethod.GET)
public SomeObject getsth(Principal principal) {
    ....
}

当调用getsth方法时,我可以看到传入了一个OAuth2Authentication对象。但是,缺少用户id、用户电话号码等用户信息。

我的问题:如何获取所有用户信息?有没有办法自定义主体对象?

谢谢,

【问题讨论】:

  • 感谢怪人回答我的问题。但答案不是我想要的。我实际上已经在我的 oauth 服务器 A 端实现了自定义的 UserDetails。它类似于“公共类用户扩展实现 UserDetails”。一切正常。我的问题是我有另一个受 oauth 保护的应用程序 B。它是一个独立的 Spring Boot 应用程序,带有 @EnableOAuth2Resource 注释。我上面提到的代码片段“public SomeObject getsth(Principal principal)”是在服务器B端。我的问题是如何在应用 B 上获取用户对象而不是主体。
  • 我也有同样的情况。 Principal 只包含经过身份验证的用户的名称。我也想获取用户 id/uuid/email,这是我自己的 UserDetailsS​​ervice 的一部分。 @YongJiang Zhang,你有没有按预期工作?
  • 我也有和你一样的情况 Filip,你有没有找到获取用户ID的方法?太诡异了,默认不支持因为userId常用

标签: spring-security spring-security-oauth2 spring-cloud


【解决方案1】:

我在同样的问题上苦苦挣扎。不过,我让它工作了。

我使用的是 JWT,所以可能与您正在执行的操作略有不同,但概念是相同的。

首先,我创建了一个自定义的 TokenServices 来获取用户的额外信息并将其添加到身份验证对象中:

public class TafTokenServices extends DefaultTokenServices {

@Override
public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {

    final TafUserDetails tafUserDetails = (TafUserDetails)authentication.getPrincipal();
    final Map<String, Object> tafInfo = new HashMap<>();
    tafInfo.put("EMAIL", tafUserDetails.getEmailAddress());
    authentication.setDetails(tafInfo);
    return super.createAccessToken(authentication);
}

}

然后配置您的身份验证服务器以使用它:

 @Configuration
@EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager).accessTokenConverter(jwtAccessTokenConverter())
                                                              .tokenServices(tafTokenServices());
    }

    @Bean
    public AuthorizationServerTokenServices tafTokenServices() {
        final TafTokenServices tafTokenServices = new TafTokenServices();
        final JwtTokenStore jwtTokenStore = new JwtTokenStore(this.jwtAccessTokenConverter());
        tafTokenServices.setTokenStore(jwtTokenStore);
        tafTokenServices.setTokenEnhancer(this.jwtAccessTokenConverter());
        return tafTokenServices;
    }

同样在身份验证服务器中,您需要使用您自己的 AccessTokenCoverter 对象将数据从身份验证对象传输到令牌中:

public class TafJwtAccessTokenConverter extends JwtAccessTokenConverter {

private static final String EMAIL_KEY = "EMAIL";

@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
    final Map<String, Object> authDetails = (Map<String, Object>)authentication.getDetails();
    ((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(authDetails);
    return super.enhance(accessToken, authentication);
}

@Override
public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
    final OAuth2Authentication authentication =    super.extractAuthentication(map);
    final Map<String, String> details = new HashMap<>();
    details.put(EMAIL_KEY, (String)map.get(EMAIL_KEY));
    authentication.setDetails(details);
    return authentication;
    }
}

注意:enhanced() 方法是在创建令牌期间调用的,因此您需要在身份验证服务器中使用它。

注意:extractAuthentication() 在身份验证期间在下游服务中调用,因此该实现也需要存在。你需要配置你的资源服务器来使用这个 AccessTokenConverter。

这将使信息进入令牌以传递到下游。请注意,我不想将数据保留在自定义用户对象中,因为我不想让我的其他服务依赖于该对象。

下一步是从令牌中取出东西并在您的资源服务器中使用它。为此,您可以重写 extractAuthentication() 以从地图中获取详细信息并将其放入身份验证对象中。通过执行以下操作,它们现在将在您的应用程序中可用:

private String getEmail() {
    final OAuth2Authentication auth = this.getAuthentication();
    final OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)auth.getDetails();
    final Map<String, Object> map = (Map)details.getDecodedDetails();
    return "Your email address is " + map.get("EMAIL");
}

private OAuth2Authentication getAuthentication() {
    return (OAuth2Authentication)SecurityContextHolder.getContext().getAuthentication();
}

我不确定这是正确的方法,它有点繁琐。我在这里有一个问题开放,有一个关于它的讨论:

https://github.com/spring-cloud/spring-cloud-security/issues/85#issuecomment-165498497

【讨论】:

    【解决方案2】:

    要自定义它,您必须提供自己的 UserDetailsS​​ervice 类:

    @Service
    public class MyUserDetailsService implements UserDetailsService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return userRepository.findByUsername(username);
    }
    }
    

    User 是您的实体,包含您需要的所有字段:

    public class User implements Serializable, UserDetails {...
    

    那么您必须配置 spring security 才能实际使用您的自定义用户详细信息服务。类似于:

    @Configuration
    @EnableWebMvcSecurity
    @EnableGlobalAuthentication
    public  class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Autowired
    private MyUserDetailsService myUserDetailsService;
    
    @Autowired
    private AuthenticationManager authenticationManager;
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .parentAuthenticationManager(authenticationManager)
                .userDetailsService(myUserDetailsService);
    }
    

    【讨论】:

    • 不是我要找的。​​span>
    猜你喜欢
    • 2014-09-01
    • 2014-12-14
    • 2013-07-23
    • 2013-06-07
    • 1970-01-01
    • 2018-02-20
    • 2017-01-31
    • 2017-06-06
    • 2020-05-01
    相关资源
    最近更新 更多