oauth2-resource-server授权配置介绍

1、背景

当了解这篇文章授权服务器后,对授权服务器有一定的认识,那么授权服务器生成token后,该怎么用呢,这就涉及到资源服务器,现在给大家简单介绍实现过程。

2、方案

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
        </dependency>

2.1 基于官网配置

首先先配置 issuer-uri ,这里指向是授权服务器的地址

# 授权服务器地址
spring.security.oauth2.resourceserver.jwt.issuer-uri= http://localhost:9000

关于过滤器链的配置:

@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class ResourceServerConfig {

	// @formatter:off
	@Bean
	SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http
			.securityMatcher("/messages/**")
				.authorizeHttpRequests()
					.requestMatchers("/messages/**").hasAuthority("SCOPE_message.read")
					.and()
			.oauth2ResourceServer()
				.jwt();
		return http.build();
	}
	// @formatter:on

}

资源服务器将使用此 issuer-uri 进一步进行自我配置,发现授权服务器的公钥,并传入用于验证JWT的JwtDecoder。此过程的结果是授权服务器必须启动并接收请求才能使资源服务器成功启动。 

若如果资源服务器必须能够独立于授权服务器启动,那么可以提供jwk-set-uri。这将是我们进一步在OAuth2安全配置中添加属性:

# 资源服务器必须能够独立于授权服务器启动
spring.security.oauth2.resourceserver.jwt.jwk-set-uri= http://localhost:9000/oauth2/jwks

至此,资源服务器可以正常使用。若是你的系统比较庞大,每次请求都会请求授权服务器,这会给授权服务器带来压力,具体的实现方案可以根据实际情况而定。

2.2 确定加密方式

授权服务器的操作:

# JWK 配置
cloud:
  jwk:
    rsa:
      # 公钥
      public-key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzt2qx7HwpblnbwtRV9ZiaXwvUO4JlOYYVFEDmINxp5d+Nbih8hmOrlM+4qhr5Ej1Gc+D+TJ2oNoN/wtUaGP38Kf21UhiEbRedRfdQvOEuDXmScLUBd4tJ9dH4+i5XFSnkZi3L0rlTmPLpv8SuJSnNg/POjxmBxBAnJq4qmm2c7bXqcXOa8oAQYMtBHtuA6hhbbD2tpZEr4tM010eDPrZysFeVKNFlNV9fgBL5H2s7GRqVslHEmU6vQfQGCXL2Z+jDv8WH3k3kneV7EL24IipMDjmPzxk5zw6L8uzKxANuLVnh3mCDkqnaPMN4TBTSqE5muKR5SHyGF0/dqxodmanWwIDAQAB
      # 私钥
      private-key: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDO3arHsfCluWdvC1FX1mJpfC9Q7gmU5hhUUQOYg3Gnl341uKHyGY6uUz7iqGvkSPUZz4P5Mnag2g3/C1RoY/fwp/bVSGIRtF51F91C84S4NeZJwtQF3i0n10fj6LlcVKeRmLcvSuVOY8um/xK4lKc2D886PGYHEECcmriqabZzttepxc5rygBBgy0Ee24DqGFtsPa2lkSvi0zTXR4M+tnKwV5Uo0WU1X1+AEvkfazsZGpWyUcSZTq9B9AYJcvZn6MO/xYfeTeSd5XsQvbgiKkwOOY/PGTnPDovy7MrEA24tWeHeYIOSqdo8w3hMFNKoTma4pHlIfIYXT92rGh2ZqdbAgMBAAECggEAHBX2GbO70lGdofLIQfk466JxLXc2tNEX94aiStgkcQESoR+RWTSFbdSejUPEQOkeNzljQnef/znBEbdg5+VpZHKhgsFCPgNzmZ6Pq1yIUJb8zldAgGVy/bLvBJn0ZKftB57+K/0VV0hu/hwpLLH+ESB3Xaw/8UXlx9KtL3Hifst94yMNh2+g1qHK7BIBxVlZmm8y137lt/ZJCCKf7zDOBFFmm/Hy71K7C6uOGmRmJ7Thq7f2dJx8iecPvMv7Bzd+LzvuZdJjowZkzMXPv85hUEpbToEQUHt4FReWWk87OUSRaXUZVONZvzUHMrYqN84tvONSa+swnxGWvjK2rRcSdQKBgQDyFeqQGPzJQY2u0rhMuj12yDTssSV7xagw88KYkaWpireuSEjxJpIEhRxjO3Ow0TYvZM9t7g/6gvuYh30/FIND4AwAuRtjAjKzXfOlIG7dJMBEk5yY1tloGwxak8sa8z+H+jw9cJik+X5j6MULNzUb709UPgMstxEVrCfhgEal/QKBgQDawYXIq1q43qIuFdjFOnZ6qBHtqWtIi8lZcOSUtPuqivIVSJt7AnkNhR9MfhkgaEuoL55wBR0BA+DoZWFmObgi5Y+Udb2+nlZVufY/ksNyZ1i+4ITKhzns7qjG2W0KMh7MG0biNbTtBy54WQp+Myog1UbuXMqyMSIunwRhOC5WNwKBgQDaDK+IN1mJlUgezaI/SgkOsmopP645e+Fwpj8C2T1UJqQnkOhSfaFL/PGC2AvumaKqsay8oY823z/rNS604K8TNfzZseFfHp24Pcm1VC9HdVDQ8/w7FlogkSxhcXmhvrPcsKIN3RtAjZEwQHsrDQEDNlxHzsthPUtgL/6D+NypqQKBgQDWN2Y8ENkBajrk6E3jLZHyIP2Vfy9o1kJxTKT6npRH+FKB1x0yg0Rpoe/5Zw5R9vAHncIILmmtoc+vo/+SLHmN6sEEf5w0uzcOMdHbhWvRbzSvjotbDLsDst4iA67MqjIQa7GpOTCYYEz4WzF0TcQ2bMRODR4NCyJPBzcxwONXVwKBgH8iJJtdmftjFqOkqNLT6XzLdPE4grkSzd2/RGiK003L3n0CxE2UqzNW/oSikAO5Jc/Tsxv6FtL9o/0Kjhut05C96Ku1oJfm7QI9ORSgCl/9Lv97GMw4E9nCTsgXqND6FMI7UqdgywpAo2hJSyE7N4vieulSJWJd5GaW/ubILwAM

将配置的公钥私钥加载到bean中,如下:

 @Autowired
    private JwkKeyProperties jwkKeyProperties;

    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        RSAKey rsaKey = new RSAKey.Builder(jwkKeyProperties.rsaPublicKey())
                .privateKey(jwkKeyProperties.privateKey())
                .build();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return new ImmutableJWKSet<>(jwkSet);
    }

至此,授权服务器固定好公钥私钥后,我们将这些公钥、私钥用到资源服务器,如下:

# JWK 配置
cloud:
  jwk:
    rsa:
      # 公钥
      public-key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzt2qx7HwpblnbwtRV9ZiaXwvUO4JlOYYVFEDmINxp5d+Nbih8hmOrlM+4qhr5Ej1Gc+D+TJ2oNoN/wtUaGP38Kf21UhiEbRedRfdQvOEuDXmScLUBd4tJ9dH4+i5XFSnkZi3L0rlTmPLpv8SuJSnNg/POjxmBxBAnJq4qmm2c7bXqcXOa8oAQYMtBHtuA6hhbbD2tpZEr4tM010eDPrZysFeVKNFlNV9fgBL5H2s7GRqVslHEmU6vQfQGCXL2Z+jDv8WH3k3kneV7EL24IipMDjmPzxk5zw6L8uzKxANuLVnh3mCDkqnaPMN4TBTSqE5muKR5SHyGF0/dqxodmanWwIDAQAB
      # 私钥
      private-key: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDO3arHsfCluWdvC1FX1mJpfC9Q7gmU5hhUUQOYg3Gnl341uKHyGY6uUz7iqGvkSPUZz4P5Mnag2g3/C1RoY/fwp/bVSGIRtF51F91C84S4NeZJwtQF3i0n10fj6LlcVKeRmLcvSuVOY8um/xK4lKc2D886PGYHEECcmriqabZzttepxc5rygBBgy0Ee24DqGFtsPa2lkSvi0zTXR4M+tnKwV5Uo0WU1X1+AEvkfazsZGpWyUcSZTq9B9AYJcvZn6MO/xYfeTeSd5XsQvbgiKkwOOY/PGTnPDovy7MrEA24tWeHeYIOSqdo8w3hMFNKoTma4pHlIfIYXT92rGh2ZqdbAgMBAAECggEAHBX2GbO70lGdofLIQfk466JxLXc2tNEX94aiStgkcQESoR+RWTSFbdSejUPEQOkeNzljQnef/znBEbdg5+VpZHKhgsFCPgNzmZ6Pq1yIUJb8zldAgGVy/bLvBJn0ZKftB57+K/0VV0hu/hwpLLH+ESB3Xaw/8UXlx9KtL3Hifst94yMNh2+g1qHK7BIBxVlZmm8y137lt/ZJCCKf7zDOBFFmm/Hy71K7C6uOGmRmJ7Thq7f2dJx8iecPvMv7Bzd+LzvuZdJjowZkzMXPv85hUEpbToEQUHt4FReWWk87OUSRaXUZVONZvzUHMrYqN84tvONSa+swnxGWvjK2rRcSdQKBgQDyFeqQGPzJQY2u0rhMuj12yDTssSV7xagw88KYkaWpireuSEjxJpIEhRxjO3Ow0TYvZM9t7g/6gvuYh30/FIND4AwAuRtjAjKzXfOlIG7dJMBEk5yY1tloGwxak8sa8z+H+jw9cJik+X5j6MULNzUb709UPgMstxEVrCfhgEal/QKBgQDawYXIq1q43qIuFdjFOnZ6qBHtqWtIi8lZcOSUtPuqivIVSJt7AnkNhR9MfhkgaEuoL55wBR0BA+DoZWFmObgi5Y+Udb2+nlZVufY/ksNyZ1i+4ITKhzns7qjG2W0KMh7MG0biNbTtBy54WQp+Myog1UbuXMqyMSIunwRhOC5WNwKBgQDaDK+IN1mJlUgezaI/SgkOsmopP645e+Fwpj8C2T1UJqQnkOhSfaFL/PGC2AvumaKqsay8oY823z/rNS604K8TNfzZseFfHp24Pcm1VC9HdVDQ8/w7FlogkSxhcXmhvrPcsKIN3RtAjZEwQHsrDQEDNlxHzsthPUtgL/6D+NypqQKBgQDWN2Y8ENkBajrk6E3jLZHyIP2Vfy9o1kJxTKT6npRH+FKB1x0yg0Rpoe/5Zw5R9vAHncIILmmtoc+vo/+SLHmN6sEEf5w0uzcOMdHbhWvRbzSvjotbDLsDst4iA67MqjIQa7GpOTCYYEz4WzF0TcQ2bMRODR4NCyJPBzcxwONXVwKBgH8iJJtdmftjFqOkqNLT6XzLdPE4grkSzd2/RGiK003L3n0CxE2UqzNW/oSikAO5Jc/Tsxv6FtL9o/0Kjhut05C96Ku1oJfm7QI9ORSgCl/9Lv97GMw4E9nCTsgXqND6FMI7UqdgywpAo2hJSyE7N4vieulSJWJd5GaW/ubILwAM

接下来是资源服务器的过滤器链配置:

@EnableGlobalMethodSecurity(prePostEnabled=true)
@EnableWebSecurity
@Configuration
public class ResourceServerConfig {

//	@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
	String issuerUri;

	@Autowired
	private JwkKeyProperties jwkKeyProperties;

	@Bean
	SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http
				.authorizeHttpRequests((authorize) -> authorize
						// SecurityConstant.IGNORE_PERM_URLS 不需要权限认证
						.requestMatchers(RedisOAuth2Constant.IGNORE_PERM_URLS).permitAll()
						// 其他的需要权限认证
						.anyRequest().authenticated()
				)
		// 资源服务配置秘钥
		.oauth2ResourceServer().jwt(oauth2ResourceServer -> {
			RSAPublicKey rsaPublicKey = jwkKeyProperties.rsaPublicKey();
			NimbusJwtDecoder.PublicKeyJwtDecoderBuilder publicKeyJwtDecoderBuilder = NimbusJwtDecoder
					.withPublicKey(rsaPublicKey);
			NimbusJwtDecoder nimbusJwtDecoder = publicKeyJwtDecoderBuilder.build();
			oauth2ResourceServer.decoder(nimbusJwtDecoder);
			// 解析jwt
			oauth2ResourceServer.jwtAuthenticationConverter(getJwtToUserAuthenticationConverter());
		});
		return http.build();
	}

	public JwtToUserAuthenticationConverter getJwtToUserAuthenticationConverter(){
		return new JwtToUserAuthenticationConverter();
	}

}

每次请求资源服务器的时候,手动根据公钥解析、转行jwt。这时候需要实现 Converter 接口,实现如下:

@Slf4j
//@Component
public class JwtToUserAuthenticationConverter implements Converter<Jwt, UsernamePasswordAuthenticationToken> {

    @Override
    public UsernamePasswordAuthenticationToken convert(@NotNull Jwt jwt) {
        System.out.println("------"+ JSON.toJSONString(jwt));
        /**
         * 方案一:从 jwt 中的 claims - >authorities 获取权限信息
         * 方案二: 根据 jwt.getSubject() 登录名去查数据库对应的用户权限
         */

        CustomUserDetails details =  new CustomUserDetails();
        // todo 调用用户接口
        String subject = jwt.getSubject();
        log.info("从jwt获取的用户名:{}",subject);
        details.setUsername(subject);
        /**
         * hasRole  对应的是 ROLE_admin
         * hasAuthority 对应的是 user:list
         *
         * 在做权限的时候需要注意 security 权限前缀
         */
        details.setRoles(List.of("admin","user:list","user"));
        return new UsernamePasswordAuthenticationToken(details, jwt, details.getAuthorities());
    }

}

至此,资源服务器的授权操作及jwt的解析完成。

3、结论

不管根据哪种方案实现,其实都需要了解授权过程。也就是授权(token中存放用户名、授权信息),接着资源服务器的token处理过程,可以直接请求授权服务器,由授权服务器验证;也可以在授权服务器确定固定的公钥私钥,资源服务器自己根据公钥解析token,获取jwt,最后获取用户信息。

以上的源码来源自己开源项目:

oauth2-resource资源服务器

oauth2-server 授权服务器

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
spring-security-oauth2-authorization-server是一个基于Spring Security的OAuth 2.0授权服务器,用于为客户端提供安全的访问资源的授权服务。您可以按照以下步骤使用该库: 1. 在您的Spring Boot项目中添加以下依赖项: ```xml <dependency> <groupId>org.springframework.security.oauth.boot</groupId> <artifactId>spring-security-oauth2-autoconfigure</artifactId> <version>2.1.0.RELEASE</version> </dependency> ``` 2. 配置您的授权服务器 在您的Spring Boot应用程序中创建一个配置类,并使用@EnableAuthorizationServer注释启用授权服务器。例如: ```java @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { // ... } ``` 3. 配置您的客户端 您可以使用ClientDetailsServiceConfigurer配置您的客户端。例如: ```java @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("client") .secret("{noop}secret") .authorizedGrantTypes("authorization_code", "refresh_token") .scopes("read", "write") .autoApprove(true) .redirectUris("http://localhost:8080/login/oauth2/code/") .and() .withClient("resource") .secret("{noop}secret") .authorizedGrantTypes("client_credentials") .scopes("read"); } ``` 上面的代码将在内存中配置两个客户端:一个用于授权授权,另一个用于客户端凭证授权。 4. 配置您的用户 您可以使用UserDetailsServiceConfigurer配置您的用户。例如: ```java @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager) .userDetailsService(userDetailsService); } ``` 上面的代码将使用authenticationManager进行身份验证,并使用userDetailsService获取用户信息。 5. 启动您的应用程序 现在,您可以启动您的应用程序,并访问http://localhost:8080/oauth/authorize以获取授权码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值