The background,

In the previous section, we learned the use of Spring Authorization Server. Here, we simply recorded the use of Spring resource Server.

Second, the demand for

The resource server provides two resources,userInfo and Hello. UserInfo: A resource is a protected resource that can be accessed only by the user.userInfo permission. Hello: A resource is a public resource and can be accessed without permission.

Three, the analysis

1. How to verify that the token accessed in the resource server is valid?

Only JWT tokens are considered here.

The token is issued by the authorization server and has been signed. Therefore, the resource server needs the JWK information of the authorization server to verify the token. Here, JwtDecoder can be configured and JWK set URI can be filled in.

2. Where in the request did the token come from? The token can be obtained from the request header or request Query Param, so you need to configure BearerTokenResolver to implement this.

3, the permission field in the token is prefixed with SCOPE_ by default. Configuration JwtAuthenticationConverter object.

4. How to add value to JWT claim? Operate via JwtDecoder#setClaimSetConverter. You can also delete the contents of the claim.

5. How to verify whether JWT is legal? Operate with the JwtDecoder#setJwtValidator method.

6, how to set the JWK timeout from the authorization server? Through JwkSetUriJwtDecoderBuilder# restOperations to operate.

Fourth, resource server authentication process

1, the request will beBearerTokenAuthenticationFilterInterceptor intercepts and resolves from ittokenIf not resolved, it is processed by the next filter. Parse it out and build oneBearerTokenAuthenticationTokenObject. 2. The next step will beHttpServletRequestPassed to theAuthenticationManagerResolverObject from which to selectAuthenticationManagerObject, and then willBearerTokenAuthenticationTokenPassed to theAuthenticationManagerObject for authentication.AuthenticationManagerThe implementation of the object depends on what our token object isJWTor opaque token3. The verification fails

  1. emptySecurityContextHolderObject.
  2. Under theAuthenticationFailureHandlerObject handling.

4. Verification is successful

  1. willAuthenticationObject set toSecurityContextHolderIn the.
  2. Pass to the remaining filter to continue processing.

Five, the realization of resource server

1. Introduce jar packages

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

2. Resource server configuration

package com.huan.study.resource.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtValidators;
import org.springframework.security.oauth2.jwt.MappedJwtClaimSetConverter;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;

import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Collections;

/** * Resource server configuration **@authorHuan.fu 2021/7/16-5:00pm */
@EnableWebSecurity
public class ResourceServerConfig extends WebSecurityConfigurerAdapter {

    private static final Logger log = LoggerFactory.getLogger(ResourceServerConfig.class);

    @Autowired
    private RestTemplateBuilder restTemplateBuilder;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                // For userInfo this API requires s
                .antMatchers("/userInfo").access("hasAuthority('user.userInfo')")
                .and()
                .oauth2ResourceServer()
                .jwt()
                // Decode JWT messages
                .decoder(jwtDecoder(restTemplateBuilder))
                // Convert the JWT information to a JwtAuthenticationToken object
                .jwtAuthenticationConverter(jwtAuthenticationConverter())
                .and()
                // Get the token from the request location
                .bearerTokenResolver(bearerTokenResolver())
                // Authentication failed
                .authenticationEntryPoint((request, response, exception) -> {
                    // OAuth2 authentication failure, there is also a non-OAuth2 authentication failure, such as no token passing, but access protected methods
                    if (exception instanceof OAuth2AuthenticationException) {
                        OAuth2AuthenticationException oAuth2AuthenticationException = (OAuth2AuthenticationException) exception;
                        OAuth2Error error = oAuth2AuthenticationException.getError();
                        log.info("Authentication failed, exception type :[{}], exception :[{}]", exception.getClass().getName(), error);
                    }
                    response.setCharacterEncoding(StandardCharsets.UTF_8.name());
                    response.setContentType(MediaType.APPLICATION_JSON.toString());
                    response.getWriter().write("{\"code\":-3,\"message\":\" You do not have access \"}");
                })
                // After successful authentication, no access permission is available
                .accessDeniedHandler((request, response, exception) -> {
                    log.info("You do not have permission to access, exception type :[{}]", exception.getClass().getName());
                    response.setCharacterEncoding(StandardCharsets.UTF_8.name());
                    response.setContentType(MediaType.APPLICATION_JSON.toString());
                    response.getWriter().write("{\"code\":-4,\"message\":\" You do not have access \"}"); }); }/** * Get the token */ from where in the request
    private BearerTokenResolver bearerTokenResolver(a) {
        DefaultBearerTokenResolver bearerTokenResolver = new DefaultBearerTokenResolver();
        // Set the parameters of the request header from which the token is obtained
        bearerTokenResolver.setBearerTokenHeaderName(HttpHeaders.AUTHORIZATION);
        bearerTokenResolver.setAllowFormEncodedBodyParameter(false);
        // Whether tokens can be obtained from URI request parameters
        bearerTokenResolver.setAllowUriQueryParameter(false);
        return bearerTokenResolver;
    }

    /** * Get permissions from JWT scope unprefix SCOPE_ * Set permissions from that field in JWT Claim * If you need to get permissions from multiple fields or from url requests, You will need to provide their own jwtAuthenticationConverter () implementation of this method, * *@return JwtAuthenticationConverter
     */
    private JwtAuthenticationConverter jwtAuthenticationConverter(a) {
        JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
        JwtGrantedAuthoritiesConverter authoritiesConverter = new JwtGrantedAuthoritiesConverter();
        // remove the prefix SCOPE_
        authoritiesConverter.setAuthorityPrefix("");
        // Get permissions from that field in JWT Claim. The schema is from scope or SCP
        authoritiesConverter.setAuthoritiesClaimName("scope");
        converter.setJwtGrantedAuthoritiesConverter(authoritiesConverter);
        return converter;
    }

    /** * JWT decoder **@return JwtDecoder
     */
    public JwtDecoder jwtDecoder(RestTemplateBuilder builder) {
        // Authorization server JWK information
        NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri("http://qq.com:8080/oauth2/jwks")
                // Set the timeout period for obtaining JWK information
                .restOperations(
                        builder.setReadTimeout(Duration.ofSeconds(3))
                                .setConnectTimeout(Duration.ofSeconds(3))
                                .build()
                )
                .build();
        // Verify the JWT
        decoder.setJwtValidator(JwtValidators.createDefault());
        // Add value to JWT claim
        decoder.setClaimSetConverter(
                MappedJwtClaimSetConverter.withDefaults(Collections.singletonMap("Add key to claim", custom -> "Value")));returndecoder; }}Copy the code

There is a lot of customization to the resource server here, so the configuration is long.

3, resources,

One protected resource and one non-protected resource.

@RestController
public class UserController {

    /** * This is a protected resource that requires the user.userinfo privilege to access. * /
    @GetMapping("userInfo")
    public Map<String, Object> userInfo(@AuthenticationPrincipal Jwt principal) {
        return new HashMap<String, Object>(4) {{
            put("principal", principal);
            put("userInfo"."Get user information");
        }};
    }

    /** * Non-protected resources */
    @GetMapping("hello")
    public String hello(a) {
        return "Hello does not require a protected resource"; }}Copy the code

Six, test,

1. Access non-protected resources

You can see that no token is required for access.

2. Access protected resources

1. Access is denied without token. 2. Then generate a token through the authorization server used in the previous article. 3, After the token access, you can return cash and access resources. 4. Demonstration can add value to the token claim. 5, presentations,userInfoIs the need touser.userInfoPermissions.

Vii. Complete code

1. Authorization server, which is the authorization server in the previous article gitee.com/huan1993/sp… 2. Resource server gitee.com/huan1993/sp…

Viii. Reference documents

1, the docs. Spring. IO/spring – secu…