Spring Security OAuth2 - 如何使用 OAuth2Authentication 对象?

Posted

技术标签:

【中文标题】Spring Security OAuth2 - 如何使用 OAuth2Authentication 对象?【英文标题】:Spring Security OAuth2 - How to use OAuth2Authentication object? 【发布时间】:2016-09-29 09:21:18 【问题描述】:

我有提供用户信息的 OAuth2 授权服务器:

public class User implements Serializable, UserDetails 
    private Long userID;
    private String username;
    private String password;
    private String fullName;
    private String email;
    private String avatar;
    private boolean enabled;
    // etc


@RestController
@RequestMapping("/api")
public class APIController 

    @RequestMapping("/me")
    public User me(@AuthenticationPrincipal User activeUser) 
        return activeUser;
    

我还实现了 OAuth2 客户端作为单独的 Spring Boot 应用程序。

@Configuration
@EnableOAuth2Sso
public class OAuth2ClientConfig extends WebSecurityConfigurerAdapter 

    @Override
    public void configure(HttpSecurity http) throws Exception 
        http.logout()
            .and()
            .antMatcher("/**").authorizeRequests()
            .antMatchers("/login").permitAll()
            .anyRequest().authenticated();
    

application.yml

security:
  user:
    password: none
  oauth2:
    client:
      clientId:     acme
      clientSecret: acmepassword
      accessTokenUri:       http://localhost:9080/sso/oauth/token
      userAuthorizationUri: http://localhost:9080/sso/oauth/authorize
    resource:
      userInfoUri:    http://localhost:9080/sso/api/me

用户认证成功:

@Controller
public class MainController 

    @RequestMapping(value = "/")
    public String index(Principal principal) 
        System.out.println(principal);
        // org.springframework.security.oauth2.provider.OAuth2Authentication@c2e723e8: Principal: superadmin; Credentials: [PROTECTED]; Authenticated: true; Details: remoteAddress=<ADDRESS>, sessionId=<SESSION>, tokenType=bearertokenValue=<TOKEN>; Granted Authorities: userRoleID=1, authority=ROLE_SUPERUSER
        OAuth2Authentication auth = (OAuth2Authentication) principal;
        System.out.println(auth.getUserAuthentication().getDetails());
        // userID=1, username=superadmin, password=***, fullName=SuperUser, email=superadmin@example.org, avatar=null, enabled=true ...
        return "index";
    

但我不明白如何在我的应用程序中使用提供的 OAuth2Authentication 对象。几乎没用。

当我尝试使用任何 Thymeleaf 安全标签时

<span sec:authentication="principal.fullName">Username</span>
<span sec:authentication="principal.authorities">Authorities</span>
<span sec:authentication="principal.userAuthentication.details.fullName">Usernames</span>

..出现以下异常:

Error retrieving value for property "property name here" of authentication object of class org.springframework.security.oauth2.provider.OAuth2Authentication

标准 Spring Security 方法 isUserInRole() 也不起作用:

System.out.println(servletRequest.isUserInRole("ROLE_SUPERUSER"));
// false

我应该实现自定义 Thymeleaf 安全方言和 hasRole() 方法吗?或者可能存在更简单的解决方案?

【问题讨论】:

principal(如authentication.getPrincipal())通常是String,所以我怀疑它有不同的属性。 @M.感谢您的评论!找到了! Thymeleaf 标签不起作用,因为方言操作的 security.core.Authentication 对象不包含 UserAuthentication 字段(这是我的自定义属性存储的地方)。而且我相信isUserInRole() 方法不起作用,因为我使用了自定义GrantedAuthority 对象。所以我只需要将Principal 替换为User 并将权限转换为适当的集合。 【参考方案1】:

好的,经过大量挖掘,我找到了解决方案。

长话短说:ResourceServerTokenServices.loadAuthentication() 方法应该被覆盖以从 OAuth2 资源服务器响应中提取自定义主体和/或权限。主要逻辑封装在extractAuthentication()方法中。

配置

@Configuration
@EnableOAuth2Sso
public class OAuth2ClientConfig extends WebSecurityConfigurerAdapter 

    @Autowired
    private ResourceServerProperties sso;

    @Autowired
    private OAuth2RestOperations restTemplate;

    @Override
    public void configure(HttpSecurity http) throws Exception 
        http.logout().and().antMatcher("/**").authorizeRequests().antMatchers("/login").permitAll().anyRequest()
                        .authenticated();
    

    @Bean
    // very important notice: method name should be exactly "userInfoTokenServices"
    public ResourceServerTokenServices userInfoTokenServices() 
        CustomUserInfoTokenServices serv = new CustomUserInfoTokenServices(sso.getUserInfoUri(), sso.getClientId());
        serv.setTokenType(sso.getTokenType());
        serv.setRestTemplate(restTemplate);
        return serv;
    

服务

public class CustomUserInfoTokenServices implements ResourceServerTokenServices 
    // exactly the same as org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices
    // except extractAuthentication() method

PS

新的 Spring Boot 版本提供了更灵活的 API。请参阅PrincipalExtractor 接口。不幸的是,它仅在 2 周前添加,当前稳定的 1.3.5.RELEASE 版本不支持。

希望对你有帮助

【讨论】:

你在auth服务器上调用/oauth/check_token吗?【参考方案2】:

使用ResourceServerTokenServicesaccess_token获取主体

@Autowired
ResourceServerTokenServices  resourceServerTokenServices;

OAuth2Authentication auth = resourceServerTokenServices.loadAuthentication(tokenId);
String principal = auth.getName();

【讨论】:

以上是关于Spring Security OAuth2 - 如何使用 OAuth2Authentication 对象?的主要内容,如果未能解决你的问题,请参考以下文章

Spring-Security OAuth2 设置 - 无法找到 oauth2 命名空间处理程序

Spring Security OAuth2 v5:NoSuchBeanDefinitionException:'org.springframework.security.oauth2.jwt.Jwt

针对授权标头的Spring Security OAuth2 CORS问题

OAuth2.0学习(4-1)Spring Security OAuth2.0 - 代码分析

Spring Security---Oauth2详解

如何使用spring-security,oauth2调整实体的正确登录?