SoFunction
Updated on 2025-04-05

Spring Clou Integration Security + Oauth2 + jwt Detailed Process of Implementation of Permission Authentication

Preface

OAuth2 is an open standard, protocol. That is, users are allowed to allow third-party applications to access user private resources (photos, avatars, etc.) stored on a certain website. There is no need to provide the username and password to third-party applications during this process. On the Internet, our most common OAuth2 application is that various third parties log in through QQ authorization, WeChat authorization, Weibo authorization, etc.
The OAuth2 protocol supports 4 different authorization modes.

  • Authorization code mode: Common third-party platform login functions basically use this mode.
  • Simplified mode: Simplified mode does not want the client server (third-party application server) to participate, and directly apply for a token from the authorized server in the browser.
  • Password mode: Password mode is when the user tells the user name and password directly to the client, and the client uses this information to apply for a token from the authorized server. This requires users to trust the client highly.
  • Client mode: Client mode refers to the client applying for authorization from the server using its own name instead of the user's name.

Important parameters of OAuth2
①response_type
code: Indicates that the authorization code is required to be returned. token: means to return the token directly
②client_id
Client identity
③client_secret
Client Key
④redirect_uri
Redirect address
⑤scope
Indicates the scope of authorization. read: read-only permission, all read-write permission
⑥grant_type
Indicates the authorization method. AUTHORIZATION_CODE (authorization code), PASSWORD (password), CLIENT_CREDENTIALS (product official), REFRESH_TOKEN (update token)
⑦state
A random number passed by the application to prevent CSRF attacks

Just go to the code without saying much

1. Create a project

First create a body:
SpringSecurityOauth2jtw
Subproject:
admin-Login
oauth2—Permissions
common--Public
gateway—Gateway

2. Steps

1. Introduce dependencies

The code is as follows (example):
Subject dependency:

<>1.8</>
        <>2.7.0</>
        <>2021.0.3</>
        <>2021.0.1.0</>
        <>2.2.</>
 			<!--MysqlDatabase Driver-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${}</version>
            </dependency>
            <!--SpringDataToolkit-->
            <dependency>
                <groupId></groupId>
                <artifactId>spring-data-commons</artifactId>
                <version>${}</version>
            </dependency>
            <!--JWT(Json Web Token)Login support-->
            <dependency>
                <groupId></groupId>
                <artifactId>jjwt</artifactId>
                <version>${}</version>
            </dependency>
            <!--JWT(Json Web Token)Login support-->
            <dependency>
                <groupId></groupId>
                <artifactId>nimbus-jose-jwt</artifactId>
                <version>${}</version>
            </dependency>
             <!--integrateddruidConnection pool-->
            <dependency>
                <groupId></groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>${}</version>
            </dependency>
            <!--Hutool JavaToolkit-->
            <dependency>
                <groupId></groupId>
                <artifactId>hutool-all</artifactId>
                <version>${}</version>
            </dependency>
            <!--Spring Cloud Related dependencies-->
            <dependency>
                <groupId></groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--Spring Cloud Alibaba Related dependencies-->
            <dependency>
                <groupId></groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

Common dependency import:

<dependency>
            <groupId></groupId>
            <artifactId>pagehelper</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-data-commons</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>logstash-logback-encoder</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

oauth2 dependency import:

<dependency>
            <groupId></groupId>
            <artifactId>common</artifactId>
            <version>1.0-SNAPSHOT</version>
            <exclusions>
                <exclusion>
                    <groupId></groupId>
                    <artifactId>spring-boot-starter-data-redis</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
            <version>${}</version>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>nimbus-jose-jwt</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>feign-okhttp</artifactId>
        </dependency>

admin dependency import:

<dependency>
            <groupId></groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>feign-okhttp</artifactId>
        </dependency>

Code section

The code is as follows (example):
Configure authentication manager

@Component
public class AuthorizationManager implements ReactiveAuthorizationManager&lt;AuthorizationContext&gt; {
    @Autowired
    private RedisTemplate&lt;String, Object&gt; redisTemplate;
    @Autowired
    private IgnoreUrlsConfig ignoreUrlsConfig;
    @Override
    public Mono&lt;AuthorizationDecision&gt; check(Mono&lt;Authentication&gt; mono, AuthorizationContext authorizationContext) {
        ServerHttpRequest request = ().getRequest();
        URI uri = ();
        PathMatcher pathMatcher = new AntPathMatcher();
        //The whitelist path is released directly        List&lt;String&gt; ignoreUrls = ();
        for (String ignoreUrl : ignoreUrls) {
            if ((ignoreUrl, ())) {
                return (new AuthorizationDecision(true));
            }
        }
        //Responding to cross-domain pre-check requests are directly released        if(()==){
            return (new AuthorizationDecision(true));
        }
        //Logins in different user systems are not allowed to access each other        try {
            String token = ().getFirst(AuthConstant.JWT_TOKEN_HEADER);
            if((token)){
                return (new AuthorizationDecision(false));
            }
            String realToken = (AuthConstant.JWT_TOKEN_PREFIX, "");
            JWSObject jwsObject = (realToken);
            String userStr = ().toString();
            UserDto userDto = (userStr, );
            if (AuthConstant.ADMIN_CLIENT_ID.equals(()) &amp;&amp; !(AuthConstant.ADMIN_URL_PATTERN, ())) {
                return (new AuthorizationDecision(false));
            }
            if (AuthConstant.PORTAL_CLIENT_ID.equals(()) &amp;&amp; (AuthConstant.ADMIN_URL_PATTERN, ())) {
                return (new AuthorizationDecision(false));
            }
        } catch (ParseException e) {
            ();
            return (new AuthorizationDecision(false));
        }
        //Not managed paths are released directly        if (!(AuthConstant.ADMIN_URL_PATTERN, ())) {
            return (new AuthorizationDecision(true));
        }
        //The management side path needs to be verified        Map&lt;Object, Object&gt; resourceRolesMap = ().entries(AuthConstant.RESOURCE_ROLES_MAP_KEY);
        Iterator&lt;Object&gt; iterator = ().iterator();
        List&lt;String&gt; authorities = new ArrayList&lt;&gt;();
        while (()) {
            String pattern = (String) ();
            if ((pattern, ())) {
                ((, (pattern)));
            }
        }
        authorities = ().map(i -&gt; i = AuthConstant.AUTHORITY_PREFIX + i).collect(());
        //Users with passed authentication and matching roles can access the current path        return mono
                .filter(Authentication::isAuthenticated)
                .flatMapIterable(Authentication::getAuthorities)
                .map(GrantedAuthority::getAuthority)
                .any(authorities::contains)
                .map(AuthorizationDecision::new)
                .defaultIfEmpty(new AuthorizationDecision(false));
    }

Return result when not logged in or token expires:

@Component
public class RestAuthenticationEntryPoint implements ServerAuthenticationEntryPoint {
    @Override
    public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException e) {
        ServerHttpResponse response = ();
        ();
        ().set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        ().set("Access-Control-Allow-Origin","*");
        ().set("Cache-Control","no-cache");
        String body= ((()));
        DataBuffer buffer =  ().wrap((("UTF-8")));
        return ((buffer));
    }
}

Return results for unauthorized access:

@Component
public class RestfulAccessDeniedHandler implements ServerAccessDeniedHandler {
    @Override
    public Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException denied) {
        ServerHttpResponse response = ();
        ();
        ().set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        ().set("Access-Control-Allow-Origin","*");
        ().set("Cache-Control","no-cache");
        String body= ((()));
        DataBuffer buffer =  ().wrap((("UTF-8")));
        return ((buffer));
    }
}

Global cross-domain configuration: Note: The front-end needs to configure when calling from the gateway

@Configuration
public class GlobalCorsConfig {
    @Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        ("*");
        ("*");
        ("*");
        (true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        ("/**", config);
        return new CorsWebFilter(source);
    }
}

Gateway whitelist configuration:

@Data
@EqualsAndHashCode(callSuper = false)
@Component
@ConfigurationProperties(prefix="")
public class IgnoreUrlsConfig {
    private List<String> urls;
}

Resource server configuration:

@AllArgsConstructor
@Configuration
@EnableWebFluxSecurity
public class ResourceServerConfig {
    private final AuthorizationManager authorizationManager;
    private final IgnoreUrlsConfig ignoreUrlsConfig;
    private final RestfulAccessDeniedHandler restfulAccessDeniedHandler;
    private final RestAuthenticationEntryPoint restAuthenticationEntryPoint;
    private final IgnoreUrlsRemoveJwtFilter ignoreUrlsRemoveJwtFilter;
    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http.oauth2ResourceServer().jwt()
                .jwtAuthenticationConverter(jwtAuthenticationConverter());
        //Customize the result of processing JWT request header expired or signature error        http.oauth2ResourceServer().authenticationEntryPoint(restAuthenticationEntryPoint);
        //Display the whitelist path and directly remove the JWT request header        (ignoreUrlsRemoveJwtFilter,);
        ()
                //Whitelist configuration                .pathMatchers(((),)).permitAll()
                // Authentication Manager configuration                .anyExchange().access(authorizationManager)
                .and().exceptionHandling()
                // Handle unauthorized                .accessDeniedHandler(restfulAccessDeniedHandler)
                //Processing unauthenticated                .authenticationEntryPoint(restAuthenticationEntryPoint)
                .and().csrf().disable();
        return ();
    }
    @Bean
    public Converter&lt;Jwt, ? extends Mono&lt;? extends AbstractAuthenticationToken&gt;&gt; jwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        (AuthConstant.AUTHORITY_PREFIX);
        (AuthConstant.AUTHORITY_CLAIM_NAME);
        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        (jwtGrantedAuthoritiesConverter);
        return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
    }
}

Swagger resource configuration:

@Slf4j
@Component
@Primary
@AllArgsConstructor
public class SwaggerResourceConfig implements SwaggerResourcesProvider {
    private final RouteLocator routeLocator;
    private final GatewayProperties gatewayProperties;
    @Override
    public List&lt;SwaggerResource&gt; get() {
        List&lt;SwaggerResource&gt; resources = new ArrayList&lt;&gt;();
        List&lt;String&gt; routes = new ArrayList&lt;&gt;();
        //Get the ID of all routes        ().subscribe(route -&gt; (()));
        //Filter out the routes defined in the configuration file ->Filter out Path Route Predicate->Stipped into api-docs path according to the path ->Generate SwaggerResource        ().stream().filter(routeDefinition -&gt; (())).forEach(route -&gt; {
            ().stream()
                    .filter(predicateDefinition -&gt; ("Path").equalsIgnoreCase(()))
                    .forEach(predicateDefinition -&gt; (swaggerResource((),
                            ().get(NameUtils.GENERATED_NAME_PREFIX + "0")
                                    .replace("**", "v2/api-docs"))));
        });
        return resources;
    }
    private SwaggerResource swaggerResource(String name, String location) {
        ("name:{},location:{}", name, location);
        SwaggerResource swaggerResource = new SwaggerResource();
        (name);
        (location);
        ("2.0");
        return swaggerResource;
    }
}

Customize the various configuration nodes of Swagger:

@RestController
public class SwaggerHandler {
    @Autowired(required = false)
    private SecurityConfiguration securityConfiguration;
    @Autowired(required = false)
    private UiConfiguration uiConfiguration;
    private final SwaggerResourcesProvider swaggerResources;
    @Autowired
    public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
         = swaggerResources;
    }
    /**
      * Swagger security configuration, support oauth and apiKey settings
      */
    @GetMapping("/swagger-resources/configuration/security")
    public Mono&lt;ResponseEntity&lt;SecurityConfiguration&gt;&gt; securityConfiguration() {
        return (new ResponseEntity&lt;&gt;(
                (securityConfiguration).orElse(().build()), ));
    }
    /**
      * Swagger UI configuration
      */
    @GetMapping("/swagger-resources/configuration/ui")
    public Mono&lt;ResponseEntity&lt;UiConfiguration&gt;&gt; uiConfiguration() {
        return (new ResponseEntity&lt;&gt;(
                (uiConfiguration).orElse(().build()), ));
    }
    /**
      * Swagger resource configuration, api-docs information for each service in the microservice
      */
    @GetMapping("/swagger-resources")
    public Mono&lt;ResponseEntity&gt; swaggerResources() {
        return ((new ResponseEntity&lt;&gt;((), )));
    }
}

Convert the JWT of the logged-in user into a global filter for user information:

@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
    private static Logger LOGGER = ();
    @Override
    public Mono&lt;Void&gt; filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = ().getHeaders().getFirst(AuthConstant.JWT_TOKEN_HEADER);
        if ((token)) {
            return (exchange);
        }
        try {
            //Analyze user information from token and set it to the header            String realToken = (AuthConstant.JWT_TOKEN_PREFIX, "");
            JWSObject jwsObject = (realToken);
            String userStr = ().toString();
            ("() user:{}",userStr);
            ServerHttpRequest request = ().mutate().header(AuthConstant.USER_TOKEN_HEADER, userStr).build();
            exchange = ().request(request).build();
        } catch (ParseException e) {
            ();
        }
        return (exchange);
    }
    @Override
    public int getOrder() {
        return 0;
    }
}

Remove the filter for JWT request header when accessing the whitelist path:

@Component
public class IgnoreUrlsRemoveJwtFilter implements WebFilter {
    @Autowired
    private IgnoreUrlsConfig ignoreUrlsConfig;
    @Override
    public Mono&lt;Void&gt; filter(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpRequest request = ();
        URI uri = ();
        PathMatcher pathMatcher = new AntPathMatcher();
        //Whitelist path removes JWT request header        List&lt;String&gt; ignoreUrls = ();
        for (String ignoreUrl : ignoreUrls) {
            if ((ignoreUrl, ())) {
                request = ().mutate().header(AuthConstant.JWT_TOKEN_HEADER, "").build();
                exchange = ().request(request).build();
                return (exchange);
            }
        }
        return (exchange);
    }
}

yml configuration:

spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true #Use lowercase service-id      routes: #Configure routing paths        - id: oauth
          uri: lb://oauth
          predicates:
            - Path=/oauth/**
           filters:
             - StripPrefix=1
         - id: admin
           uri: lb://admin # Here is the configuration of nacos
           Predicates:
             - Path=/admin/**
           filters:
             - StripPrefix=1
   security:
     oauth2:
       resourceserver:
         jwt:
           jwk-set-uri: 'http://localhost:8201/oauth/rsa/publicKey' #Configure the public key access address of RSA
   redis:
     host: localhost # Redis server address
     database: 0 # Redis database index (default is 0)
     port: 6379 # Redis server connection port
     password: # Redis server connection password (default is empty)
     timeout: 3000ms # Connection timeout (milliseconds)
 Secure:
   ignore:
     urls: #Configure whitelist path
       - "/"
       - "/swagger-resources/**"
       - "/swagger/**"
       - "/*/v2/api-docs"
      - "/*/*.js"
      - "/*/*.css"
      - "/*/*.png"
      - "/*/*.ico"
      - "/webjars/**"
      - "/actuator/**"
      - "/oauth/oauth/token"
      - "/oauth/rsa/publicKey"
      - "/admin/admin/login"
      - "/admin/admin/register"
management: #Enable SpringBoot Admin monitoring  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    health:
      show-details: always
logging:
  level:
    root: info
    : debug
logstash:
  host: localhost
#spring:
#  application:
#    name: gateway
#  cloud:
#    nacos:
#      config:
#        server-addr: 127.0.0.1:8848
#        username: nacos
#        password: nacos
#        namespace: 4e903430-c64b-4c68-a43c-59478dd173e6
#        group: DEFAULT_GROUP
#        prefix: ${}
#        file-extension: yaml

3.oauth2 code part

JWT Content Enhancer:

@Component
public class JwtTokenEnhancer implements TokenEnhancer {
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        SecurityUser securityUser = (SecurityUser) ();
        Map&lt;String, Object&gt; info = new HashMap&lt;&gt;();
        //Set the user ID to JWT        ("id", ());
        ("client_id",());
        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);
        return accessToken;
    }
}

Authentication server configuration:

@AllArgsConstructor
@Configuration
@EnableAuthorizationServer
public class Oauth2ServerConfig extends AuthorizationServerConfigurerAdapter {
    private final PasswordEncoder passwordEncoder;
    private final UserServiceImpl userDetailsService;
    private final AuthenticationManager authenticationManager;
    private final JwtTokenEnhancer jwtTokenEnhancer;
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        ()
                .withClient("admin-app")
                .secret(("123456"))
                .scopes("all")
                .authorizedGrantTypes("password", "refresh_token")
                .accessTokenValiditySeconds(3600*24)
                .refreshTokenValiditySeconds(3600*24*7)
                .and()
                .withClient("portal-app")
                .secret(("123456"))
                .scopes("all")
                .authorizedGrantTypes("password", "refresh_token")
                .accessTokenValiditySeconds(3600*24)
                .refreshTokenValiditySeconds(3600*24*7);
    }
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List&lt;TokenEnhancer&gt; delegates = new ArrayList&lt;&gt;();
        (jwtTokenEnhancer);
        (accessTokenConverter());
        (delegates); //Configure JWT content enhancer        (authenticationManager)
                .userDetailsService(userDetailsService) //Configure the service to load user information                .accessTokenConverter(accessTokenConverter())
                .tokenEnhancer(enhancerChain);
    }
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        ();
    }
    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        (keyPair());
        return jwtAccessTokenConverter;
    }
    @Bean
    public KeyPair keyPair() {
        //Get the key pair from the certificate under the classpath        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource(""), "123456".toCharArray());
        return ("jwt", "123456".toCharArray());
    }
}

Swagger related configuration:

@Configuration
@EnableSwagger2
public class SwaggerConfig extends BaseSwaggerConfig {
    @Override
    public SwaggerProperties swaggerProperties() {
        return ()
                .apiBasePackage(".")
                .title("Certification Center")
                .description("Certification Center-related interface documents")
                .contactName("oauth")
                .version("1.0")
                .enableSecurity(true)
                .build();
    }
    @Bean
    public BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
        return generateBeanPostProcessor();
    }
}

SpringSecurity related configuration:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig{
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return ();
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

Message constant definition:

public class MessageConstant {
    public static final String LOGIN_SUCCESS = "Login successfully!";
    public static final String USERNAME_PASSWORD_ERROR = "Incorrect username or password!";
    public static final String CREDENTIALS_EXPIRED = "The login credentials for this account have expired, please log in again!";
    public static final String ACCOUNT_DISABLED = "This account has been disabled, please contact the administrator!";
    public static final String ACCOUNT_LOCKED = "This account has been locked, please contact the administrator!";
    public static final String ACCOUNT_EXPIRED = "This account has expired, please contact the administrator!";
    public static final String PERMISSION_DENIED = "No access permission, please contact the administrator!";
}

Customize Oauth2 get token interface:

@RestController
@Api(tags = "AuthController", description = "Certification Center Login Authentication")
@RequestMapping("/oauth")
public class AuthController {
    @Autowired
    private TokenEndpoint tokenEndpoint;
    @ApiOperation("Oauth2 gets token")
    @RequestMapping(value = "/token", method = )
    public CommonResult&lt;Oauth2TokenDto&gt; postAccessToken(HttpServletRequest request,
                                                        @ApiParam("Authorization Mode") @RequestParam String grant_type,
                                                        @ApiParam("Oauth2 Client ID") @RequestParam String client_id,
                                                        @ApiParam("Oauth2 client key") @RequestParam String client_secret,
                                                        @ApiParam("Refresh token") @RequestParam(required = false) String refresh_token,
                                                        @ApiParam("Login username") @RequestParam(required = false) String username,
                                                        @ApiParam("Login Password") @RequestParam(required = false) String password) throws HttpRequestMethodNotSupportedException {
        Map&lt;String, String&gt; parameters = new HashMap&lt;&gt;();
        ("grant_type",grant_type);
        ("client_id",client_id);
        ("client_secret",client_secret);
        ("refresh_token",refresh_token);
        ("username",username);
        ("password",password);
        OAuth2AccessToken oAuth2AccessToken = ((), parameters).getBody();
        Oauth2TokenDto oauth2TokenDto = ()
                .token(())
                .refreshToken(().getValue())
                .expiresIn(())
                .tokenHead(AuthConstant.JWT_TOKEN_PREFIX).build();
        return (oauth2TokenDto);
    }
}

Get RSA public key interface:

@RestController
@Api(tags = "KeyPairController", description = "Get RSA public key interface")
@RequestMapping("/rsa")
public class KeyPairController {
    @Autowired
    private KeyPair keyPair;
    @GetMapping("/publicKey")
    public Map&lt;String, Object&gt; getKey() {
        RSAPublicKey publicKey = (RSAPublicKey) ();
        RSAKey key = new (publicKey).build();
        return new JWKSet(key).toJSONObject();
    }
}

Oauth2 gets the token return information encapsulation:

@Data
@EqualsAndHashCode(callSuper = false)
@Builder
public class Oauth2TokenDto {
    @ApiModelProperty("Access Token")
    private String token;
    @ApiModelProperty("Flash token")
    private String refreshToken;
    @ApiModelProperty("Access token header prefix")
    private String tokenHead;
    @ApiModelProperty("Effective time (seconds)")
    private int expiresIn;
}

Login user information:

@Data
public class SecurityUser implements UserDetails {
    /**
     * ID
     */
    private Long id;
    /**
      * username
      */
    private String username;
    /**
      * User password
      */
    private String password;
    /**
      * User status
      */
    private Boolean enabled;
    /**
      * Login client ID
      */
    private String clientId;
    /**
      * Permission data
      */
    private Collection&lt;SimpleGrantedAuthority&gt; authorities;
    public SecurityUser() {
    }
    public SecurityUser(UserDto userDto) {
        (());
        (());
        (());
        (() == 1);
        (());
        if (() != null) {
            authorities = new ArrayList&lt;&gt;();
            ().forEach(item -&gt; (new SimpleGrantedAuthority(item)));
        }
    }
    @Override
    public Collection&lt;? extends GrantedAuthority&gt; getAuthorities() {
        return ;
    }
    @Override
    public String getPassword() {
        return ;
    }
    @Override
    public String getUsername() {
        return ;
    }
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return ;
    }
}

Globally handle exceptions thrown by Oauth2:

@ControllerAdvice
public class Oauth2ExceptionHandler {
    @ResponseBody
    @ExceptionHandler(value = )
    public CommonResult handleOauth2(OAuth2Exception e) {
        return (());
    }
}

Backend user service remotely calls Service:

@FeignClient("admin")
public interface UmsAdminService {
    @GetMapping("/admin/loadByUsername")
    UserDto loadUserByUsername(@RequestParam String username);
}

User management business category:

@Service
public class UserServiceImpl implements UserDetailsService {
    @Autowired
    private UmsAdminService adminService;
    @Autowired
    private HttpServletRequest request;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        String clientId = ("client_id");
        UserDto userDto = null;
        if(AuthConstant.ADMIN_CLIENT_ID.equals(clientId)){
            userDto = (username);
        }
        if (userDto==null) {
            throw new UsernameNotFoundException(MessageConstant.USERNAME_PASSWORD_ERROR);
        }
        (clientId);
        SecurityUser securityUser = new SecurityUser(userDto);
        if (!()) {
            throw new DisabledException(MessageConstant.ACCOUNT_DISABLED);
        } else if (!()) {
            throw new LockedException(MessageConstant.ACCOUNT_LOCKED);
        } else if (!()) {
            throw new AccountExpiredException(MessageConstant.ACCOUNT_EXPIRED);
        } else if (!()) {
            throw new CredentialsExpiredException(MessageConstant.CREDENTIALS_EXPIRED);
        }
        return securityUser;
    }
}

yml configuration:

spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher
management:
  endpoints:
    web:
      exposure:
        include: "*"
feign:
  okhttp:
    enabled: true
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: basic
logging:
  level:
    root: info
#spring:
#  application:
#    name: oauth
#  cloud:
#    nacos:
#      config:
#        server-addr: 127.0.0.1:8848
#        username: nacos
#        password: nacos
#        namespace: 4e903430-c64b-4c68-a43c-59478dd173e6
#        group: DEFAULT_GROUP
#        prefix: ${}
#        file-extension: yaml

Need to generate it yourself

Code section

General return object:

public class CommonResult&lt;T&gt; {
    private long code;
    private String message;
    private T data;
    protected CommonResult() {
    }
    protected CommonResult(long code, String message, T data) {
         = code;
         = message;
         = data;
    }
    /**
      * Result successfully
      *
      * @param data obtained data
      */
    public static &lt;T&gt; CommonResult&lt;T&gt; success(T data) {
        return new CommonResult&lt;T&gt;((), (), data);
    }
    /**
      * Result successfully
      *
      * @param data obtained data
      * @param message prompt message
      */
    public static &lt;T&gt; CommonResult&lt;T&gt; success(T data, String message) {
        return new CommonResult&lt;T&gt;((), message, data);
    }
    /**
      * Failed to return result
      * @param errorCode error code
      */
    public static &lt;T&gt; CommonResult&lt;T&gt; failed(IErrorCode errorCode) {
        return new CommonResult&lt;T&gt;((), (), null);
    }
    /**
      * Failed to return result
      * @param errorCode error code
      * @param message Error message
      */
    public static &lt;T&gt; CommonResult&lt;T&gt; failed(IErrorCode errorCode,String message) {
        return new CommonResult&lt;T&gt;((), message, null);
    }
    /**
      * Failed to return result
      * @param message prompt message
      */
    public static &lt;T&gt; CommonResult&lt;T&gt; failed(String message) {
        return new CommonResult&lt;T&gt;((), message, null);
    }
    /**
      * Failed to return result
      */
    public static &lt;T&gt; CommonResult&lt;T&gt; failed() {
        return failed();
    }
    /**
      * Parameter verification failed to return result
      */
    public static &lt;T&gt; CommonResult&lt;T&gt; validateFailed() {
        return failed(ResultCode.VALIDATE_FAILED);
    }
    /**
      * Parameter verification failed to return result
      * @param message prompt message
      */
    public static &lt;T&gt; CommonResult&lt;T&gt; validateFailed(String message) {
        return new CommonResult&lt;T&gt;(ResultCode.VALIDATE_FAILED.getCode(), message, null);
    }
    /**
      * Not logged in to return results
      */
    public static &lt;T&gt; CommonResult&lt;T&gt; unauthorized(T data) {
        return new CommonResult&lt;T&gt;((), (), data);
    }
    /**
      * Unauthorized return result
      */
    public static &lt;T&gt; CommonResult&lt;T&gt; forbidden(T data) {
        return new CommonResult&lt;T&gt;((), (), data);
    }
    public long getCode() {
        return code;
    }
    public void setCode(long code) {
         = code;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
         = message;
    }
    public T getData() {
        return data;
    }
    public void setData(T data) {
         = data;
    }
}

Error code for encapsulating API:

public interface IErrorCode {
    long getCode();
    String getMessage();
}

Enumerate some commonly used API opcodes:

public enum ResultCode implements IErrorCode {
    SUCCESS(200, "The operation is successful"),
    FAILED(500, "Operation failed"),
    VALIDATE_FAILED(404, "Parameter verification failed"),
    UNAUTHORIZED(401, "Not logged in yet or the token has expired"),
    FORBIDDEN(403, "No relevant permissions");
    private long code;
    private String message;
    private ResultCode(long code, String message) {
         = code;
         = message;
    }
    public long getCode() {
        return code;
    }
    public String getMessage() {
        return message;
    }
}

Redis basic configuration:

public class BaseRedisConfig {
    @Bean
    public RedisTemplate&lt;String, Object&gt; redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisSerializer&lt;Object&gt; serializer = redisSerializer();
        RedisTemplate&lt;String, Object&gt; redisTemplate = new RedisTemplate&lt;&gt;();
        (redisConnectionFactory);
        (new StringRedisSerializer());
        (serializer);
        (new StringRedisSerializer());
        (serializer);
        ();
        return redisTemplate;
    }
    @Bean
    public RedisSerializer&lt;Object&gt; redisSerializer() {
        //Create a JSON serializer        Jackson2JsonRedisSerializer&lt;Object&gt; serializer = new Jackson2JsonRedisSerializer&lt;&gt;();
        ObjectMapper objectMapper = new ObjectMapper();
        (, );
        // Must be set, otherwise JSON cannot be converted into an object, it will be converted to Map type        (,.NON_FINAL);
        (objectMapper);
        return serializer;
    }
    @Bean
    public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheWriter redisCacheWriter = (redisConnectionFactory);
        //Set the validity period of Redis cache to 1 day        RedisCacheConfiguration redisCacheConfiguration = ()
                .serializeValuesWith((redisSerializer())).entryTtl((1));
        return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
    }
    @Bean
    public RedisService redisService(){
        return new RedisServiceImpl();
    }
}

Swagger basic configuration:

public abstract class BaseSwaggerConfig {
    @Bean
    public Docket createRestApi() {
        SwaggerProperties swaggerProperties = swaggerProperties();
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo(swaggerProperties))
                .select()
                .apis((()))
                .paths(())
                .build();
        if (()) {
            (securitySchemes()).securityContexts(securityContexts());
        }
        return docket;
    }
    private ApiInfo apiInfo(SwaggerProperties swaggerProperties) {
        return new ApiInfoBuilder()
                .title(())
                .description(())
                .contact(new Contact((), (), ()))
                .version(())
                .build();
    }
    private List&lt;SecurityScheme&gt; securitySchemes() {
        //Set request header information        List&lt;SecurityScheme&gt; result = new ArrayList&lt;&gt;();
        ApiKey apiKey = new ApiKey("Authorization", "Authorization", "header");
        (apiKey);
        return result;
    }
    private List&lt;SecurityContext&gt; securityContexts() {
        //Set the path that requires login authentication        List&lt;SecurityContext&gt; result = new ArrayList&lt;&gt;();
        (getContextByPath("/*/.*"));
         return result;
     }
     private SecurityContext getContextByPath(String pathRegex) {
         Return ()
                 .securityReferences(defaultAuth())
                 .forPaths((pathRegex))
                 .build();
     }
     private List<SecurityReference> defaultAuth() {
         List<SecurityReference> result = new ArrayList<>();
         AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
         AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
         authorizationScopes[0] = authorizationScope;
         (new SecurityReference("Authorization", authorizationScopes));
         return result;
     }
     public BeanPostProcessor generateBeanPostProcessor() {
         return new BeanPostProcessor() {
             @Override
             public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                 if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
                     customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
                 }
                 return bean;
             }
             private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
                 List<T> copy = ()
                         .filter(mapping -> () == null)
                         .collect(());
                 ();
                 (copy);
             }
             @SuppressWarnings("unchecked")
             private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
                 try {
                     Field field = ((), "handlerMappings");
                     (true);
                     return (List<RequestMappingInfoHandlerMapping>) (bean);
                 } catch (IllegalArgumentException | IllegalAccessException e) {
                     throw new IllegalStateException(e);
                 }
             }
         };
     }
     /**
      * Custom Swagger configuration
      */
    public abstract SwaggerProperties swaggerProperties();
}

Permission-related constant definition:

public interface AuthConstant {
    /**
      * JWT storage permission prefix
      */
    String AUTHORITY_PREFIX = "ROLE_";
    /**
      * JWT storage permission attributes
      */
    String AUTHORITY_CLAIM_NAME = "authorities";
    /**
      * Backend client_id
      */
    String ADMIN_CLIENT_ID = "admin-app";
    /**
     * app client_id
     */
    String PORTAL_CLIENT_ID = "portal-app";
    /**
      * Background interface path matching
      */
    String ADMIN_URL_PATTERN = "/admin/**";
     /**
      * Redis cache permission rule key
      */
    String RESOURCE_ROLES_MAP_KEY = "auth:resourceRolesMap";
    /**
      * Authentication information Http request header
      */
    String JWT_TOKEN_HEADER = "Authorization";
    /**
      * JWT token prefix
      */
    String JWT_TOKEN_PREFIX = "Bearer ";
    /**
      * User information Http request header
      */
    String USER_TOKEN_HEADER = "user";
}

Swagger custom configuration:

@Data
@EqualsAndHashCode(callSuper = false)
@Builder
public class SwaggerProperties {
    /**
      * Basic path to API document generation
      */
    private String apiBasePackage;
    /**
      * Do you want to enable login authentication?
      */
    private boolean enableSecurity;
    /**
      * Document title
      */
    private String title;
    /**
      * Document description
      */
    private String description;
    /**
      * Documentation version
      */
    private String version;
    /**
      * Document contact name
      */
    private String contactName;
    /**
      * Document contact website
      */
    private String contactUrl;
    /**
      * Document contact email
      */
    private String contactEmail;
}

Login user information:

@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public class UserDto {
    private Long id;
    private String username;
    private String password;
    private Integer status;
    private String clientId;
    private List<String> roles;
}

Log encapsulation class of Controller layer:

@Data
@EqualsAndHashCode(callSuper = false)
public class WebLog {
    /**
      * Operation description
      */
    private String description;
    /**
      * Operate the user
      */
    private String username;
    /**
      * Operation time
      */
    private Long startTime;
    /**
      * Time consumed
      */
    private Integer spendTime;
    /**
      * Root path
      */
    private String basePath;
    /**
     * URI
     */
    private String uri;
    /**
     * URL
     */
    private String url;
    /**
      * Request type
      */
    private String method;
    /**
      * IP address
      */
    private String ip;
    /**
      * Request parameters
      */
    private Object parameter;
    /**
      * Return result
      */
    private Object result;
}

Custom API exception:

public class ApiException extends RuntimeException {
    private IErrorCode errorCode;
    public ApiException(IErrorCode errorCode) {
        super(());
         = errorCode;
    }
    public ApiException(String message) {
        super(message);
    }
    public ApiException(Throwable cause) {
        super(cause);
    }
    public ApiException(String message, Throwable cause) {
        super(message, cause);
    }
    public IErrorCode getErrorCode() {
        return errorCode;
    }
}

Assertion processing class, used to throw various API exceptions:

public class Asserts {
    public static void fail(String message) {
        throw new ApiException(message);
    }
    public static void fail(IErrorCode errorCode) {
        throw new ApiException(errorCode);
    }
}

Global exception handling:

@ControllerAdvice
public class GlobalExceptionHandler {
    @ResponseBody
    @ExceptionHandler(value = )
    public CommonResult handle(ApiException e) {
        if (() != null) {
            return (());
        }
        return (());
    }
    @ResponseBody
    @ExceptionHandler(value = )
    public CommonResult handleValidException(MethodArgumentNotValidException e) {
        BindingResult bindingResult = ();
        String message = null;
        if (()) {
            FieldError fieldError = ();
            if (fieldError != null) {
                message = ()+();
            }
        }
        return (message);
    }
    @ResponseBody
    @ExceptionHandler(value = )
    public CommonResult handleValidException(BindException e) {
        BindingResult bindingResult = ();
        String message = null;
        if (()) {
            FieldError fieldError = ();
            if (fieldError != null) {
                message = ()+();
            }
        }
        return (message);
    }
}

Unified log processing sections:

@Aspect
@Component
@Order(1)
public class WebLogAspect {
    private static final Logger LOGGER = ();
    @Pointcut("execution(public * com.*.*.controller.*.*(..))||execution(public * com.*.*.controller.*.*(..))")
    public void webLog() {
    }
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
    }
    @AfterReturning(value = "webLog()", returning = "ret")
    public void doAfterReturning(Object ret) throws Throwable {
    }
    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = ();
        //Get the current request object        ServletRequestAttributes attributes = (ServletRequestAttributes) ();
        HttpServletRequest request = ();
        //Record request information (passed into Elasticsearch through Logstash)        WebLog webLog = new WebLog();
        Object result = ();
        Signature signature = ();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = ();
        if (()) {
            ApiOperation log = ();
            (());
        }
        long endTime = ();
        String urlStr = ().toString();
        ((urlStr, (urlStr).getPath()));
        (());
        (());
        (getParameter(method, ()));
        (result);
        ((int) (endTime - startTime));
        (startTime);
        (());
        (().toString());
        Map&lt;String,Object&gt; logMap = new HashMap&lt;&gt;();
        ("url",());
        ("method",());
        ("parameter",());
        ("spendTime",());
        ("description",());
//        ("{}", (webLog));
        ((logMap), (webLog).toString());
        return result;
    }
    /**
      * Get request parameters based on the method and the passed parameters
      */
    private Object getParameter(Method method, Object[] args) {
        List&lt;Object&gt; argList = new ArrayList&lt;&gt;();
        Parameter[] parameters = ();
        for (int i = 0; i &lt; ; i++) {
            //Prefer the parameters modified by the RequestBody annotation as the request parameter            RequestBody requestBody = parameters[i].getAnnotation();
            if (requestBody != null) {
                (args[i]);
            }
            //Prefer the parameters modified by the RequestParam annotation as the request parameter            RequestParam requestParam = parameters[i].getAnnotation();
            if (requestParam != null) {
                Map&lt;String, Object&gt; map = new HashMap&lt;&gt;();
                String key = parameters[i].getName();
                if (!(())) {
                    key = ();
                }
                (key, args[i]);
                (map);
            }
        }
        if (() == 0) {
            return null;
        } else if (() == 1) {
            return (0);
        } else {
            return argList;
        }
    }
}

Redis Operation Service:

public interface RedisService {
    /**
      * Save properties
      */
    void set(String key, Object value, long time);
    /**
      * Save properties
      */
    void set(String key, Object value);
    /**
      * Get attributes
      */
    Object get(String key);
    /**
      * Delete attributes
      */
    Boolean del(String key);
    /**
      * Batch delete attributes
      */
    Long del(List&lt;String&gt; keys);
    /**
      * Set expiration time
      */
    Boolean expire(String key, long time);
    /**
      * Get expiration time
      */
    Long getExpire(String key);
    /**
      * Determine whether there is this attribute
      */
    Boolean hasKey(String key);
    /**
      * Increment by delta
      */
    Long incr(String key, long delta);
    /**
      * Decrease by delta
      */
    Long decr(String key, long delta);
    /**
      * Get the properties in the Hash structure
      */
    Object hGet(String key, String hashKey);
    /**
      * Put an attribute into the Hash structure
      */
    Boolean hSet(String key, String hashKey, Object value, long time);
    /**
      * Put an attribute into the Hash structure
      */
    void hSet(String key, String hashKey, Object value);
    /**
      * Get the entire Hash structure directly
      */
    Map&lt;Object, Object&gt; hGetAll(String key);
    /**
      * Directly set the entire Hash structure
      */
    Boolean hSetAll(String key, Map&lt;String, Object&gt; map, long time);
    /**
      * Directly set the entire Hash structure
      */
    void hSetAll(String key, Map&lt;String, ?&gt; map);
    /**
      * Remove attributes in the Hash structure
      */
    void hDel(String key, Object... hashKey);
    /**
      * Determine whether the property is present in the Hash structure
      */
    Boolean hHasKey(String key, String hashKey);
    /**
      * Increment of attributes in the Hash structure
      */
    Long hIncr(String key, String hashKey, Long delta);
    /**
      * Decreasing attributes in the Hash structure
      */
    Long hDecr(String key, String hashKey, Long delta);
    /**
      * Get Set structure
      */
    Set&lt;Object&gt; sMembers(String key);
    /**
      * Add properties to the Set structure
      */
    Long sAdd(String key, Object... values);
    /**
      * Add properties to the Set structure
      */
    Long sAdd(String key, long time, Object... values);
    /**
      * Is it a property in Set
      */
    Boolean sIsMember(String key, Object value);
    /**
      * Get the length of the Set structure
      */
    Long sSize(String key);
    /**
      * Remove attributes in Set structure
      */
    Long sRemove(String key, Object... values);
    /**
      * Get the properties in the List structure
      */
    List&lt;Object&gt; lRange(String key, long start, long end);
    /**
      * Get the length of the List structure
      */
    Long lSize(String key);
    /**
      * Get attributes in List based on index
      */
    Object lIndex(String key, long index);
    /**
      * Add properties to the List structure
      */
    Long lPush(String key, Object value);
    /**
      * Add properties to the List structure
      */
    Long lPush(String key, Object value, long time);
    /**
      * Add properties in batches to List structure
      */
    Long lPushAll(String key, Object... values);
    /**
      * Add properties in batches to List structure
      */
    Long lPushAll(String key, Long time, Object... values);
    /**
      * Remove attributes from List structure
      */
    Long lRemove(String key, long count, Object value);
}

Redis operation implementation class:

public class RedisServiceImpl implements RedisService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Override
    public void set(String key, Object value, long time) {
        ().set(key, value, time, );
    }
    @Override
    public void set(String key, Object value) {
        ().set(key, value);
    }
    @Override
    public Object get(String key) {
        return ().get(key);
    }
    @Override
    public Boolean del(String key) {
        return (key);
    }
    @Override
    public Long del(List<String> keys) {
        return (keys);
    }
    @Override
    public Boolean expire(String key, long time) {
        return (key, time, );
    }
    @Override
    public Long getExpire(String key) {
        return (key, );
    }
    @Override
    public Boolean hasKey(String key) {
        return (key);
    }
    @Override
    public Long incr(String key, long delta) {
        return ().increment(key, delta);
    }
    @Override
    public Long decr(String key, long delta) {
        return ().increment(key, -delta);
    }
    @Override
    public Object hGet(String key, String hashKey) {
        return ().get(key, hashKey);
    }
    @Override
    public Boolean hSet(String key, String hashKey, Object value, long time) {
        ().put(key, hashKey, value);
        return expire(key, time);
    }
    @Override
    public void hSet(String key, String hashKey, Object value) {
        ().put(key, hashKey, value);
    }
    @Override
    public Map<Object, Object> hGetAll(String key) {
        return ().entries(key);
    }
    @Override
    public Boolean hSetAll(String key, Map<String, Object> map, long time) {
        ().putAll(key, map);
        return expire(key, time);
    }
    @Override
    public void hSetAll(String key, Map<String, ?> map) {
        ().putAll(key, map);
    }
    @Override
    public void hDel(String key, Object... hashKey) {
        ().delete(key, hashKey);
    }
    @Override
    public Boolean hHasKey(String key, String hashKey) {
        return ().hasKey(key, hashKey);
    }
    @Override
    public Long hIncr(String key, String hashKey, Long delta) {
        return ().increment(key, hashKey, delta);
    }
    @Override
    public Long hDecr(String key, String hashKey, Long delta) {
        return ().increment(key, hashKey, -delta);
    }
    @Override
    public Set<Object> sMembers(String key) {
        return ().members(key);
    }
    @Override
    public Long sAdd(String key, Object... values) {
        return ().add(key, values);
    }
    @Override
    public Long sAdd(String key, long time, Object... values) {
        Long count = ().add(key, values);
        expire(key, time);
        return count;
    }
    @Override
    public Boolean sIsMember(String key, Object value) {
        return ().isMember(key, value);
    }
    @Override
    public Long sSize(String key) {
        return ().size(key);
    }
    @Override
    public Long sRemove(String key, Object... values) {
        return ().remove(key, values);
    }
    @Override
    public List<Object> lRange(String key, long start, long end) {
        return ().range(key, start, end);
    }
    @Override
    public Long lSize(String key) {
        return ().size(key);
    }
    @Override
    public Object lIndex(String key, long index) {
        return ().index(key, index);
    }
    @Override
    public Long lPush(String key, Object value) {
        return ().rightPush(key, value);
    }
    @Override
    public Long lPush(String key, Object value, long time) {
        Long index = ().rightPush(key, value);
        expire(key, time);
        return index;
    }
    @Override
    public Long lPushAll(String key, Object... values) {
        return ().rightPushAll(key, values);
    }
    @Override
    public Long lPushAll(String key, Long time, Object... values) {
        Long count = ().rightPushAll(key, values);
        expire(key, time);
        return count;
    }
    @Override
    public Long lRemove(String key, long count, Object value) {
        return ().remove(key, count, value);
    }
}

Configuration:

&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE configuration&gt;
&lt;configuration&gt;
    &lt;!--Reference default log configuration--&gt;
    &lt;include resource="org/springframework/boot/logging/logback/"/&gt;
    &lt;!--Use default console log output implementation--&gt;
    &lt;include resource="org/springframework/boot/logging/logback/"/&gt;
    &lt;!--Application name--&gt;
    &lt;springProperty scope="context" name="APP_NAME" source="" defaultValue="springBoot"/&gt;
    &lt;!--Log file saving path--&gt;
    &lt;property name="LOG_FILE_PATH" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${:-/tmp}}}/logs}"/&gt;
    &lt;!--LogStashaccesshost--&gt;
    &lt;springProperty name="LOG_STASH_HOST" scope="context" source="" defaultValue="localhost"/&gt;
    &lt;!--DEBUGLog output to file--&gt;
    &lt;appender name="FILE_DEBUG"
              class=""&gt;
        &lt;!--OutputDEBUGAbove level logs--&gt;
        &lt;filter class=""&gt;
            &lt;level&gt;DEBUG&lt;/level&gt;
        &lt;/filter&gt;
        &lt;encoder&gt;
            &lt;!--Set as the default file log format--&gt;
            &lt;pattern&gt;${FILE_LOG_PATTERN}&lt;/pattern&gt;
            &lt;charset&gt;UTF-8&lt;/charset&gt;
        &lt;/encoder&gt;
        &lt;rollingPolicy class=""&gt;
            &lt;!--Set file naming format--&gt;
            &lt;fileNamePattern&gt;${LOG_FILE_PATH}/debug/${APP_NAME}-%d{yyyy-MM-dd}-%&lt;/fileNamePattern&gt;
            &lt;!--Set the log file size,Regenerate the file if it exceeds it,default10M--&gt;
            &lt;maxFileSize&gt;${LOG_FILE_MAX_SIZE:-10MB}&lt;/maxFileSize&gt;
            &lt;!--Number of days of log file retention,default30sky--&gt;
            &lt;maxHistory&gt;${LOG_FILE_MAX_HISTORY:-30}&lt;/maxHistory&gt;
        &lt;/rollingPolicy&gt;
    &lt;/appender&gt;
    &lt;!--ERRORLog output to file--&gt;
    &lt;appender name="FILE_ERROR"
              class=""&gt;
        &lt;!--只OutputERRORLevel log--&gt;
        &lt;filter class=""&gt;
            &lt;level&gt;ERROR&lt;/level&gt;
            &lt;onMatch&gt;ACCEPT&lt;/onMatch&gt;
            &lt;onMismatch&gt;DENY&lt;/onMismatch&gt;
        &lt;/filter&gt;
        &lt;encoder&gt;
            &lt;!--Set as the default file log format--&gt;
            &lt;pattern&gt;${FILE_LOG_PATTERN}&lt;/pattern&gt;
            &lt;charset&gt;UTF-8&lt;/charset&gt;
        &lt;/encoder&gt;
        &lt;rollingPolicy class=""&gt;
            &lt;!--Set file naming format--&gt;
            &lt;fileNamePattern&gt;${LOG_FILE_PATH}/error/${APP_NAME}-%d{yyyy-MM-dd}-%&lt;/fileNamePattern&gt;
            &lt;!--Set the log file size,Regenerate the file if it exceeds it,default10M--&gt;
            &lt;maxFileSize&gt;${LOG_FILE_MAX_SIZE:-10MB}&lt;/maxFileSize&gt;
            &lt;!--Number of days of log file retention,default30sky--&gt;
            &lt;maxHistory&gt;${LOG_FILE_MAX_HISTORY:-30}&lt;/maxHistory&gt;
        &lt;/rollingPolicy&gt;
    &lt;/appender&gt;
    &lt;!--DEBUG日志Output到LogStash--&gt;
    &lt;appender name="LOG_STASH_DEBUG" class=""&gt;
        &lt;filter class=""&gt;
            &lt;level&gt;DEBUG&lt;/level&gt;
        &lt;/filter&gt;
        &lt;destination&gt;${LOG_STASH_HOST}:4560&lt;/destination&gt;
        &lt;encoder charset="UTF-8" class=""&gt;
            &lt;providers&gt;
                &lt;timestamp&gt;
                    &lt;timeZone&gt;Asia/Shanghai&lt;/timeZone&gt;
                &lt;/timestamp&gt;
                &lt;!--自定义日志Output格式--&gt;
                &lt;pattern&gt;
                    &lt;pattern&gt;
                        {
                        "project": "mall-swarm",
                        "level": "%level",
                        "service": "${APP_NAME:-}",
                        "pid": "${PID:-}",
                        "thread": "%thread",
                        "class": "%logger",
                        "message": "%message",
                        "stack_trace": "%exception{20}"
                        }
                    &lt;/pattern&gt;
                &lt;/pattern&gt;
            &lt;/providers&gt;
        &lt;/encoder&gt;
        &lt;!--When there are multipleLogStashWhen serving,设置access策略为轮询--&gt;
        &lt;connectionStrategy&gt;
            &lt;roundRobin&gt;
                &lt;connectionTTL&gt;5 minutes&lt;/connectionTTL&gt;
            &lt;/roundRobin&gt;
        &lt;/connectionStrategy&gt;
    &lt;/appender&gt;
    &lt;!--ERROR日志Output到LogStash--&gt;
    &lt;appender name="LOG_STASH_ERROR" class=""&gt;
        &lt;filter class=""&gt;
            &lt;level&gt;ERROR&lt;/level&gt;
            &lt;onMatch&gt;ACCEPT&lt;/onMatch&gt;
            &lt;onMismatch&gt;DENY&lt;/onMismatch&gt;
        &lt;/filter&gt;
        &lt;destination&gt;${LOG_STASH_HOST}:4561&lt;/destination&gt;
        &lt;encoder charset="UTF-8" class=""&gt;
            &lt;providers&gt;
                &lt;timestamp&gt;
                    &lt;timeZone&gt;Asia/Shanghai&lt;/timeZone&gt;
                &lt;/timestamp&gt;
                &lt;!--自定义日志Output格式--&gt;
                &lt;pattern&gt;
                    &lt;pattern&gt;
                        {
                        "project": "mall-swarm",
                        "level": "%level",
                        "service": "${APP_NAME:-}",
                        "pid": "${PID:-}",
                        "thread": "%thread",
                        "class": "%logger",
                        "message": "%message",
                        "stack_trace": "%exception{20}"
                        }
                    &lt;/pattern&gt;
                &lt;/pattern&gt;
            &lt;/providers&gt;
        &lt;/encoder&gt;
        &lt;!--When there are multipleLogStashWhen serving,设置access策略为轮询--&gt;
        &lt;connectionStrategy&gt;
            &lt;roundRobin&gt;
                &lt;connectionTTL&gt;5 minutes&lt;/connectionTTL&gt;
            &lt;/roundRobin&gt;
        &lt;/connectionStrategy&gt;
    &lt;/appender&gt;
    &lt;!--业务日志Output到LogStash--&gt;
    &lt;appender name="LOG_STASH_BUSINESS" class=""&gt;
        &lt;destination&gt;${LOG_STASH_HOST}:4562&lt;/destination&gt;
        &lt;encoder charset="UTF-8" class=""&gt;
            &lt;providers&gt;
                &lt;timestamp&gt;
                    &lt;timeZone&gt;Asia/Shanghai&lt;/timeZone&gt;
                &lt;/timestamp&gt;
                &lt;!--自定义日志Output格式--&gt;
                &lt;pattern&gt;
                    &lt;pattern&gt;
                        {
                        "project": "mall-swarm",
                        "level": "%level",
                        "service": "${APP_NAME:-}",
                        "pid": "${PID:-}",
                        "thread": "%thread",
                        "class": "%logger",
                        "message": "%message",
                        "stack_trace": "%exception{20}"
                        }
                    &lt;/pattern&gt;
                &lt;/pattern&gt;
            &lt;/providers&gt;
        &lt;/encoder&gt;
        &lt;!--When there are multipleLogStashWhen serving,设置access策略为轮询--&gt;
        &lt;connectionStrategy&gt;
            &lt;roundRobin&gt;
                &lt;connectionTTL&gt;5 minutes&lt;/connectionTTL&gt;
            &lt;/roundRobin&gt;
        &lt;/connectionStrategy&gt;
    &lt;/appender&gt;
    &lt;!--接口access记录日志Output到LogStash--&gt;
    &lt;appender name="LOG_STASH_RECORD" class=""&gt;
        &lt;destination&gt;${LOG_STASH_HOST}:4563&lt;/destination&gt;
        &lt;encoder charset="UTF-8" class=""&gt;
            &lt;providers&gt;
                &lt;timestamp&gt;
                    &lt;timeZone&gt;Asia/Shanghai&lt;/timeZone&gt;
                &lt;/timestamp&gt;
                &lt;!--自定义日志Output格式--&gt;
                &lt;pattern&gt;
                    &lt;pattern&gt;
                        {
                        "project": "mall-swarm",
                        "level": "%level",
                        "service": "${APP_NAME:-}",
                        "class": "%logger",
                        "message": "%message"
                        }
                    &lt;/pattern&gt;
                &lt;/pattern&gt;
            &lt;/providers&gt;
        &lt;/encoder&gt;
        &lt;!--When there are multipleLogStashWhen serving,设置access策略为轮询--&gt;
        &lt;connectionStrategy&gt;
            &lt;roundRobin&gt;
                &lt;connectionTTL&gt;5 minutes&lt;/connectionTTL&gt;
            &lt;/roundRobin&gt;
        &lt;/connectionStrategy&gt;
    &lt;/appender&gt;
    &lt;!--控制框架Output日志--&gt;
    &lt;logger name="org.slf4j" level="INFO"/&gt;
    &lt;logger name="springfox" level="INFO"/&gt;
    &lt;logger name="" level="INFO"/&gt;
    &lt;logger name="" level="INFO"/&gt;
    &lt;logger name="" level="INFO"/&gt;
    &lt;logger name="" level="INFO"/&gt;
    &lt;root level="DEBUG"&gt;
        &lt;appender-ref ref="CONSOLE"/&gt;
        &lt;appender-ref ref="FILE_DEBUG"/&gt;
        &lt;appender-ref ref="FILE_ERROR"/&gt;
        &lt;appender-ref ref="LOG_STASH_DEBUG"/&gt;
        &lt;appender-ref ref="LOG_STASH_ERROR"/&gt;
    &lt;/root&gt;
    &lt;logger name="com.*.*." level="DEBUG"&gt;
        &lt;appender-ref ref="LOG_STASH_RECORD"/&gt;
    &lt;/logger&gt;
    &lt;logger name="com.*.*" level="DEBUG"&gt;
        &lt;appender-ref ref="LOG_STASH_BUSINESS"/&gt;
    &lt;/logger&gt;
&lt;/configuration&gt;

Code section

Swagger API documentation related configuration:

@Configuration
@EnableSwagger2
public class SwaggerConfig extends BaseSwaggerConfig {
    @Override
    public SwaggerProperties swaggerProperties() {
        return ()
                .apiBasePackage("")
                .title("Background System")
                .description("Background-related interface documents")
                .contactName("admin")
                .version("1.0")
                .enableSecurity(true)
                .build();
    }
    @Bean
    public BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
        return generateBeanPostProcessor();
    }
}

Backend user management:

@Controller
@Api(tags = "UmsAdminController", description = "Backend User Management")
@RequestMapping("/admin")
public class UmsAdminController {
    @Autowired
    private UmsAdminService adminService;
    @ApiOperation(value = "User Registration")
    @RequestMapping(value = "/register", method = )
    @ResponseBody
    public CommonResult&lt;UmsAdmin&gt; register(@Validated @RequestBody UmsAdminParam umsAdminParam) {
        UmsAdmin umsAdmin = (umsAdminParam);
        if (umsAdmin == null) {
            return ();
        }
        return (umsAdmin);
    }
    @ApiOperation(value = "Return token after login")
    @RequestMapping(value = "/login", method = )
    @ResponseBody
    public CommonResult login(@Validated @RequestBody UmsAdminLoginParam umsAdminLoginParam) {
        return ((),());
    }
    @ApiOperation(value = "Login function")
    @RequestMapping(value = "/logout", method = )
    @ResponseBody
    public CommonResult logout() {
        return (null);
    }
    @ApiOperation("Get general user information based on username")
    @RequestMapping(value = "/loadByUsername", method = )
    @ResponseBody
    public UserDto loadUserByUsername(@RequestParam String username) {
        UserDto userDTO = (username);
        return userDTO;
    }
}

UmsAdminService implementation class:

@Service
public class UmsAdminServiceImpl implements UmsAdminService {
    private static final Logger LOGGER = ();
    @Autowired
    private UmsAdminMapper adminMapper;
    @Autowired
    private UmsAdminLoginLogMapper loginLogMapper;
    @Autowired
    private AuthService authService;
    @Autowired
    private HttpServletRequest request;
    @Override
    public UmsAdmin register(UmsAdminParam umsAdminParam) {
        UmsAdmin umsAdmin = new UmsAdmin();
        (umsAdminParam, umsAdmin);
        (new Date());
        (1);
        //Query whether users with the same username        List&lt;UmsAdmin&gt; umsAdminList = (new QueryWrapper&lt;UmsAdmin&gt;().eq("username",()));
        if (() &gt; 0) {
            return null;
        }
        //Encrypt the password        String encodePassword = (());
        (encodePassword);
        (umsAdmin);
        return umsAdmin;
    }
    @Override
    public CommonResult login(String username, String password) {
        if((username)||(password)){
            ("The username or password cannot be empty!");
        }
        Map&lt;String, String&gt; params = new HashMap&lt;&gt;();
        ("client_id", AuthConstant.ADMIN_CLIENT_ID);
        ("client_secret","123456");
        ("grant_type","password");
        ("username",username);
        ("password",password);
        CommonResult restResult = (params);
        if(()==()&amp;&amp;()!=null){
        }
        return restResult;
    }
    @Override
    public UserDto loadUserByUsername(String username) {
        //Get user information        UmsAdmin admin = getAdminByUsername(username);
        if (admin != null) {
            UserDto userDTO = new UserDto();
            (admin,userDTO);
            return userDTO;
        }
        return null;
    }
    @Override
    public UmsAdmin getAdminByUsername(String username) {
        List&lt;UmsAdmin&gt; adminList = (new QueryWrapper&lt;UmsAdmin&gt;().eq("username",username));
        if (adminList != null &amp;&amp; () &gt; 0) {
            return (0);
        }
        return null;
    }
}

The authentication service remotely calls the Service:

@FeignClient("oauth")
public interface AuthService {
    @PostMapping(value = "/oauth/token")
    CommonResult getAccessToken(@RequestParam Map<String, String> parameters);
}

Backend Admin Service:

public interface UmsAdminService {
    /**
      * Registration function
      */
    UmsAdmin register(UmsAdminParam umsAdminParam);
    /**
      * Login function
      * @param username Username
      * @param password
      * @return Calling the authentication center to return the result
      */
    CommonResult login(String username, String password);
    /**
      * Get user information
      */
    UserDto loadUserByUsername(String username);
    /**
      * Get the background administrator based on the username
      */
    UmsAdmin getAdminByUsername(String username);
}

yml configuration:

spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher
  datasource:
    url: jdbc:mysql://localhost:3306/admin?useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=Asia/Shanghai&amp;useSSL=false
    username: root
    password: root
    druid:
      initial-size: 5 #Connection pool initialization size      min-idle: 10 #Minimum number of idle connections      max-active: 20 #Maximum number of connections      web-stat-filter:
        exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" #Don't count these request data      stat-view-servlet: #Login username and password for accessing monitoring web pages        login-username: druid
        login-password: druid
  redis:
    host: localhost # Redis Server Address    database: 0 # Redis database index (default is 0)    port: 6379 # Redis Server Connection Port    password:  # Redis server connection password (default is empty)    timeout: 3000ms # Connection timeout (milliseconds)management: #Enable SpringBoot Admin monitoring  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    health:
      show-details: always
redis:
  database: admin
  key:
    admin: 'ums:admin'
  expire:
    common: 86400 # 24 hoursfeign:
  okhttp:
    enabled: true
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: basic
mybatis-plus:
  configuration:
    log-impl: .slf4j.Slf4jImpl

Summarize

I wrote an article for the first time. If you don't understand, please give me more

The above is what we will talk about today. This article only briefly introduces the use of unified authentication, and will share the source code address in the future.
Project address: /zhouwudi/SpringSecurityOauth2

This is the article about Spring Cloud's complete integration of Security + Oauth2 + jwt to implement permission authentication. For more relevant Spring Cloud Security + Oauth2 + jwt permission authentication content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!