如何从OAuth2授权服务器/用户端点获取自定义用户信息

Ser*_*auk 19 spring spring-security spring-boot spring-cloud spring-oauth2

我有一个配置了@EnableResourceServer注释的资源服务器,它通过user-info-uri参数引用授权服务器,如下所示:

security:
  oauth2:
    resource:
      user-info-uri: http://localhost:9001/user
Run Code Online (Sandbox Code Playgroud)


授权服务器/用户端点返回的扩展名org.springframework.security.core.userdetails.User包含例如电子邮件:

{  
   "password":null,
   "username":"myuser",
    ...
   "email":"me@company.com"
}
Run Code Online (Sandbox Code Playgroud)


每当访问某个资源服务器端点时,Spring通过调用授权服务器的/user端点来验证后台的访问令牌,它实际上会返回丰富的用户信息(其中包含例如电子邮件信息,我已经使用Wireshark验证了该信息).

所以问题是如何在没有明确第二次调用授权服务器/user端点的情况下获取此自定义用户信息.Spring是否在授权后将其存储在资源服务器本地的某个位置,或者如果没有任何可用的开箱即用,那么实现此类用户信息的最佳方式是什么?

Yan*_*lem 21

解决方案是实现自定义 UserInfoTokenServices

https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoTokenServices.java

只需将您的自定义实现提供为Bean,它将被用来代替默认实现.

在这个UserInfoTokenServices中,您可以构建principal您想要的类似内容.

此UserInfoTokenServices用于从/users授权服务器的端点的响应中提取UserDetails .正如你所看到的那样

private Object getPrincipal(Map<String, Object> map) {
    for (String key : PRINCIPAL_KEYS) {
        if (map.containsKey(key)) {
            return map.get(key);
        }
    }
    return "unknown";
}
Run Code Online (Sandbox Code Playgroud)

PRINCIPAL_KEYS默认情况下,仅提取指定的属性.这就是你的问题.您必须提取的不仅仅是用户名或任何属性的名称.所以寻找更多的钥匙.

private Object getPrincipal(Map<String, Object> map) {
    MyUserDetails myUserDetails = new myUserDetails();
    for (String key : PRINCIPAL_KEYS) {
        if (map.containsKey(key)) {
            myUserDetails.setUserName(map.get(key));
        }
    }
    if( map.containsKey("email") {
        myUserDetails.setEmail(map.get("email"));
    }
    //and so on..
    return myUserDetails;
}
Run Code Online (Sandbox Code Playgroud)

布线:

@Autowired
private ResourceServerProperties sso;

@Bean
public ResourceServerTokenServices myUserInfoTokenServices() {
    return new MyUserInfoTokenServices(sso.getUserInfoUri(), sso.getClientId());
}
Run Code Online (Sandbox Code Playgroud)

!!使用Spring Boot 1.4更新事情变得越来越容易!

使用Spring Boot 1.4.0 ,引入了PrincipalExtractor.应该实现此类以提取自定义主体(请参阅Spring Boot 1.4发行说明).


小智 5

所有数据都已存在于Principal对象中,不需要第二个请求.只返回你需要的东西.我使用以下方法进行Facebook登录:

@RequestMapping("/sso/user")
@SuppressWarnings("unchecked")
public Map<String, String> user(Principal principal) {
    if (principal != null) {
        OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) principal;
        Authentication authentication = oAuth2Authentication.getUserAuthentication();
        Map<String, String> details = new LinkedHashMap<>();
        details = (Map<String, String>) authentication.getDetails();
        logger.info("details = " + details);  // id, email, name, link etc.
        Map<String, String> map = new LinkedHashMap<>();
        map.put("email", details.get("email"));
        return map;
    }
    return null;
}
Run Code Online (Sandbox Code Playgroud)