Spring Security OAuth2 JWT 资源服务器(旧版)具有 NULL Principal

Posted

技术标签:

【中文标题】Spring Security OAuth2 JWT 资源服务器(旧版)具有 NULL Principal【英文标题】:Spring Security OAuth 2 JWKS Resource Server (Legacy) has NULL Principal 【发布时间】:2020-10-28 18:01:31 【问题描述】:

我需要设置一个旧的 Spring-Boot 1.5 项目来充当 OAuth2 资源服务器,使用公开的 JWKS 来验证每个请求的所有传入 JWT。

我关注/查看的链接和文章是:

https://projects.spring.io/spring-security-oauth/docs/oauth2.html https://www.baeldung.com/spring-security-oauth2-jws-jwk Spring Security OAuth with JWK Example

我的pom.xml 中有以下依赖项:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.22.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>project</groupId>
<artifactId>project-resource</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Project Resource</name>
<description>Project Resource</description>

<properties>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-jwt</artifactId>
    </dependency>
    <!--https://projects.spring.io/spring-security-oauth/docs/oauth2.html-->
    <!--https://projects.spring.io/spring-security-oauth/docs/oauth2.html#resource-server-configuration-->
    <!--https://github.com/spring-projects/spring-security-oauth/tree/master/tests/annotation/resource-->
    <dependency>
        <groupId>org.springframework.security.oauth</groupId>
        <artifactId>spring-security-oauth2</artifactId>
        <version>2.5.0.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

我将其配置为充当资源服务器并在 application.yml 中使用 JWKS uri:

security:
  oauth2:
    resource:
      jwk:
        key-set-uri: http://sso.server/.well-known/jwks.json

主类就这么简单:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;

@SpringBootApplication
@EnableResourceServer
public class ProjectResourceApplication 
    public static void main(String[] args) 
        SpringApplication.run(ProjectResourceApplication.class, args);
    

为了使用来自另一个域的 API,我还必须提供这个CorsFilter, 因为简单的 httpSecurity.cors() 不起作用。

import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSFilter extends OncePerRequestFilter 

    @Value("$custom.cors.allowOrigin:*")
    private String allowOrigin;

    @Value("$custom.cors.allowMethods:GET, POST, PUT, DELETE, OPTIONS")
    private String allowMethods;

    @Value("$custom.cors.allowHeaders:Content-Type,Authorization")
    private String allowHeaders;

    @Value("$custom.cors.allowCredentials:true")
    private String allowCredentials;

    @Value("$custom.cors.maxAge:3600")
    private String maxAge;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException 
        response.addHeader("Access-Control-Allow-Origin", allowOrigin);

        if (HttpMethod.OPTIONS.matches(request.getMethod())) 
            response.addHeader("Access-Control-Allow-Methods", allowMethods);
            response.addHeader("Access-Control-Allow-Headers", allowHeaders);
            response.addHeader("Access-Control-Allow-Credentials", allowCredentials);
            response.addHeader("Access-Control-Max-Age", maxAge);
            response.setStatus(HttpServletResponse.SC_OK);
         else 
            filterChain.doFilter(request, response);
        
    

授权工作正常,这意味着令牌已通过 JWKS 正确验证, 但是 Principal 始终为 null。

例如:

import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.security.Principal;

@RestController
public class PrincipalController 

    @GetMapping("/principal")
    public Principal getPrincipal(@AuthenticationPrincipal Principal principal) 
        return principal;
    

有什么错误或遗漏吗?此外,如果可能的话,我还希望有一个自定义对象作为 Principal。 Rosource 服务器是否还需要有关客户端的信息才能提供委托人?如在https://github.com/spring-projects/spring-security-oauth/issues/1012

【问题讨论】:

【参考方案1】:

一种解决方案是添加UserInfoTokenServices 类型的@Bean,如下所述。

https://***.com/a/62650255/8737144 Spring OAuth2 ResourceServer external AuthorizationServer

可以使用自定义 Principal 和 Authorities 提取器进一步自定义此类 @Bean

【讨论】:

以上是关于Spring Security OAuth2 JWT 资源服务器(旧版)具有 NULL Principal的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security Oauth2

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详解