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<AuthorizationContext> { @Autowired private RedisTemplate<String, Object> redisTemplate; @Autowired private IgnoreUrlsConfig ignoreUrlsConfig; @Override public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) { ServerHttpRequest request = ().getRequest(); URI uri = (); PathMatcher pathMatcher = new AntPathMatcher(); //The whitelist path is released directly List<String> 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(()) && !(AuthConstant.ADMIN_URL_PATTERN, ())) { return (new AuthorizationDecision(false)); } if (AuthConstant.PORTAL_CLIENT_ID.equals(()) && (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<Object, Object> resourceRolesMap = ().entries(AuthConstant.RESOURCE_ROLES_MAP_KEY); Iterator<Object> iterator = ().iterator(); List<String> authorities = new ArrayList<>(); while (()) { String pattern = (String) (); if ((pattern, ())) { ((, (pattern))); } } authorities = ().map(i -> 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<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> 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<SwaggerResource> get() { List<SwaggerResource> resources = new ArrayList<>(); List<String> routes = new ArrayList<>(); //Get the ID of all routes ().subscribe(route -> (())); //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 -> (())).forEach(route -> { ().stream() .filter(predicateDefinition -> ("Path").equalsIgnoreCase(())) .forEach(predicateDefinition -> (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<ResponseEntity<SecurityConfiguration>> securityConfiguration() { return (new ResponseEntity<>( (securityConfiguration).orElse(().build()), )); } /** * Swagger UI configuration */ @GetMapping("/swagger-resources/configuration/ui") public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() { return (new ResponseEntity<>( (uiConfiguration).orElse(().build()), )); } /** * Swagger resource configuration, api-docs information for each service in the microservice */ @GetMapping("/swagger-resources") public Mono<ResponseEntity> swaggerResources() { return ((new ResponseEntity<>((), ))); } }
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<Void> 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<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { ServerHttpRequest request = (); URI uri = (); PathMatcher pathMatcher = new AntPathMatcher(); //Whitelist path removes JWT request header List<String> 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<String, Object> info = new HashMap<>(); //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<TokenEnhancer> delegates = new ArrayList<>(); (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<Oauth2TokenDto> 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<String, String> parameters = new HashMap<>(); ("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<String, Object> 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<SimpleGrantedAuthority> authorities; public SecurityUser() { } public SecurityUser(UserDto userDto) { (()); (()); (()); (() == 1); (()); if (() != null) { authorities = new ArrayList<>(); ().forEach(item -> (new SimpleGrantedAuthority(item))); } } @Override public Collection<? extends GrantedAuthority> 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<T> { 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 <T> CommonResult<T> success(T data) { return new CommonResult<T>((), (), data); } /** * Result successfully * * @param data obtained data * @param message prompt message */ public static <T> CommonResult<T> success(T data, String message) { return new CommonResult<T>((), message, data); } /** * Failed to return result * @param errorCode error code */ public static <T> CommonResult<T> failed(IErrorCode errorCode) { return new CommonResult<T>((), (), null); } /** * Failed to return result * @param errorCode error code * @param message Error message */ public static <T> CommonResult<T> failed(IErrorCode errorCode,String message) { return new CommonResult<T>((), message, null); } /** * Failed to return result * @param message prompt message */ public static <T> CommonResult<T> failed(String message) { return new CommonResult<T>((), message, null); } /** * Failed to return result */ public static <T> CommonResult<T> failed() { return failed(); } /** * Parameter verification failed to return result */ public static <T> CommonResult<T> validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * Parameter verification failed to return result * @param message prompt message */ public static <T> CommonResult<T> validateFailed(String message) { return new CommonResult<T>(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * Not logged in to return results */ public static <T> CommonResult<T> unauthorized(T data) { return new CommonResult<T>((), (), data); } /** * Unauthorized return result */ public static <T> CommonResult<T> forbidden(T data) { return new CommonResult<T>((), (), 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<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisSerializer<Object> serializer = redisSerializer(); RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); (redisConnectionFactory); (new StringRedisSerializer()); (serializer); (new StringRedisSerializer()); (serializer); (); return redisTemplate; } @Bean public RedisSerializer<Object> redisSerializer() { //Create a JSON serializer Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(); 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<SecurityScheme> securitySchemes() { //Set request header information List<SecurityScheme> result = new ArrayList<>(); ApiKey apiKey = new ApiKey("Authorization", "Authorization", "header"); (apiKey); return result; } private List<SecurityContext> securityContexts() { //Set the path that requires login authentication List<SecurityContext> result = new ArrayList<>(); (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<String,Object> logMap = new HashMap<>(); ("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<Object> argList = new ArrayList<>(); Parameter[] parameters = (); for (int i = 0; i < ; 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<String, Object> map = new HashMap<>(); 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<String> 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<Object, Object> hGetAll(String key); /** * Directly set the entire Hash structure */ Boolean hSetAll(String key, Map<String, Object> map, long time); /** * Directly set the entire Hash structure */ void hSetAll(String key, Map<String, ?> 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<Object> 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<Object> 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:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration> <configuration> <!--Reference default log configuration--> <include resource="org/springframework/boot/logging/logback/"/> <!--Use default console log output implementation--> <include resource="org/springframework/boot/logging/logback/"/> <!--Application name--> <springProperty scope="context" name="APP_NAME" source="" defaultValue="springBoot"/> <!--Log file saving path--> <property name="LOG_FILE_PATH" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${:-/tmp}}}/logs}"/> <!--LogStashaccesshost--> <springProperty name="LOG_STASH_HOST" scope="context" source="" defaultValue="localhost"/> <!--DEBUGLog output to file--> <appender name="FILE_DEBUG" class=""> <!--OutputDEBUGAbove level logs--> <filter class=""> <level>DEBUG</level> </filter> <encoder> <!--Set as the default file log format--> <pattern>${FILE_LOG_PATTERN}</pattern> <charset>UTF-8</charset> </encoder> <rollingPolicy class=""> <!--Set file naming format--> <fileNamePattern>${LOG_FILE_PATH}/debug/${APP_NAME}-%d{yyyy-MM-dd}-%</fileNamePattern> <!--Set the log file size,Regenerate the file if it exceeds it,default10M--> <maxFileSize>${LOG_FILE_MAX_SIZE:-10MB}</maxFileSize> <!--Number of days of log file retention,default30sky--> <maxHistory>${LOG_FILE_MAX_HISTORY:-30}</maxHistory> </rollingPolicy> </appender> <!--ERRORLog output to file--> <appender name="FILE_ERROR" class=""> <!--只OutputERRORLevel log--> <filter class=""> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <encoder> <!--Set as the default file log format--> <pattern>${FILE_LOG_PATTERN}</pattern> <charset>UTF-8</charset> </encoder> <rollingPolicy class=""> <!--Set file naming format--> <fileNamePattern>${LOG_FILE_PATH}/error/${APP_NAME}-%d{yyyy-MM-dd}-%</fileNamePattern> <!--Set the log file size,Regenerate the file if it exceeds it,default10M--> <maxFileSize>${LOG_FILE_MAX_SIZE:-10MB}</maxFileSize> <!--Number of days of log file retention,default30sky--> <maxHistory>${LOG_FILE_MAX_HISTORY:-30}</maxHistory> </rollingPolicy> </appender> <!--DEBUG日志Output到LogStash--> <appender name="LOG_STASH_DEBUG" class=""> <filter class=""> <level>DEBUG</level> </filter> <destination>${LOG_STASH_HOST}:4560</destination> <encoder charset="UTF-8" class=""> <providers> <timestamp> <timeZone>Asia/Shanghai</timeZone> </timestamp> <!--自定义日志Output格式--> <pattern> <pattern> { "project": "mall-swarm", "level": "%level", "service": "${APP_NAME:-}", "pid": "${PID:-}", "thread": "%thread", "class": "%logger", "message": "%message", "stack_trace": "%exception{20}" } </pattern> </pattern> </providers> </encoder> <!--When there are multipleLogStashWhen serving,设置access策略为轮询--> <connectionStrategy> <roundRobin> <connectionTTL>5 minutes</connectionTTL> </roundRobin> </connectionStrategy> </appender> <!--ERROR日志Output到LogStash--> <appender name="LOG_STASH_ERROR" class=""> <filter class=""> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <destination>${LOG_STASH_HOST}:4561</destination> <encoder charset="UTF-8" class=""> <providers> <timestamp> <timeZone>Asia/Shanghai</timeZone> </timestamp> <!--自定义日志Output格式--> <pattern> <pattern> { "project": "mall-swarm", "level": "%level", "service": "${APP_NAME:-}", "pid": "${PID:-}", "thread": "%thread", "class": "%logger", "message": "%message", "stack_trace": "%exception{20}" } </pattern> </pattern> </providers> </encoder> <!--When there are multipleLogStashWhen serving,设置access策略为轮询--> <connectionStrategy> <roundRobin> <connectionTTL>5 minutes</connectionTTL> </roundRobin> </connectionStrategy> </appender> <!--业务日志Output到LogStash--> <appender name="LOG_STASH_BUSINESS" class=""> <destination>${LOG_STASH_HOST}:4562</destination> <encoder charset="UTF-8" class=""> <providers> <timestamp> <timeZone>Asia/Shanghai</timeZone> </timestamp> <!--自定义日志Output格式--> <pattern> <pattern> { "project": "mall-swarm", "level": "%level", "service": "${APP_NAME:-}", "pid": "${PID:-}", "thread": "%thread", "class": "%logger", "message": "%message", "stack_trace": "%exception{20}" } </pattern> </pattern> </providers> </encoder> <!--When there are multipleLogStashWhen serving,设置access策略为轮询--> <connectionStrategy> <roundRobin> <connectionTTL>5 minutes</connectionTTL> </roundRobin> </connectionStrategy> </appender> <!--接口access记录日志Output到LogStash--> <appender name="LOG_STASH_RECORD" class=""> <destination>${LOG_STASH_HOST}:4563</destination> <encoder charset="UTF-8" class=""> <providers> <timestamp> <timeZone>Asia/Shanghai</timeZone> </timestamp> <!--自定义日志Output格式--> <pattern> <pattern> { "project": "mall-swarm", "level": "%level", "service": "${APP_NAME:-}", "class": "%logger", "message": "%message" } </pattern> </pattern> </providers> </encoder> <!--When there are multipleLogStashWhen serving,设置access策略为轮询--> <connectionStrategy> <roundRobin> <connectionTTL>5 minutes</connectionTTL> </roundRobin> </connectionStrategy> </appender> <!--控制框架Output日志--> <logger name="org.slf4j" level="INFO"/> <logger name="springfox" level="INFO"/> <logger name="" level="INFO"/> <logger name="" level="INFO"/> <logger name="" level="INFO"/> <logger name="" level="INFO"/> <root level="DEBUG"> <appender-ref ref="CONSOLE"/> <appender-ref ref="FILE_DEBUG"/> <appender-ref ref="FILE_ERROR"/> <appender-ref ref="LOG_STASH_DEBUG"/> <appender-ref ref="LOG_STASH_ERROR"/> </root> <logger name="com.*.*." level="DEBUG"> <appender-ref ref="LOG_STASH_RECORD"/> </logger> <logger name="com.*.*" level="DEBUG"> <appender-ref ref="LOG_STASH_BUSINESS"/> </logger> </configuration>
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<UmsAdmin> 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<UmsAdmin> umsAdminList = (new QueryWrapper<UmsAdmin>().eq("username",())); if (() > 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<String, String> params = new HashMap<>(); ("client_id", AuthConstant.ADMIN_CLIENT_ID); ("client_secret","123456"); ("grant_type","password"); ("username",username); ("password",password); CommonResult restResult = (params); if(()==()&&()!=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<UmsAdmin> adminList = (new QueryWrapper<UmsAdmin>().eq("username",username)); if (adminList != null && () > 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&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&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!