rengb пре 4 година
родитељ
комит
5fa12a728a
22 измењених фајлова са 949 додато и 0 уклоњено
  1. 4 0
      common/pom.xml
  2. 35 0
      common/src/main/java/com/lantone/common/exception/ApiException.java
  3. 20 0
      common/src/main/java/com/lantone/common/exception/Asserts.java
  4. 57 0
      common/src/main/java/com/lantone/common/exception/GlobalExceptionHandler.java
  5. 1 0
      pom.xml
  6. 33 0
      user-auth/.gitignore
  7. 68 0
      user-auth/pom.xml
  8. 17 0
      user-auth/src/main/java/com/lantone/UserAuthApplication.java
  9. 32 0
      user-auth/src/main/java/com/lantone/userauth/component/JwtTokenEnhancer.java
  10. 90 0
      user-auth/src/main/java/com/lantone/userauth/config/Oauth2ServerConfig.java
  11. 42 0
      user-auth/src/main/java/com/lantone/userauth/config/WebSecurityConfig.java
  12. 24 0
      user-auth/src/main/java/com/lantone/userauth/constant/MessageConstant.java
  13. 57 0
      user-auth/src/main/java/com/lantone/userauth/controller/AuthController.java
  14. 31 0
      user-auth/src/main/java/com/lantone/userauth/controller/KeyPairController.java
  15. 27 0
      user-auth/src/main/java/com/lantone/userauth/domain/Oauth2TokenDto.java
  16. 96 0
      user-auth/src/main/java/com/lantone/userauth/domain/SecurityUser.java
  17. 23 0
      user-auth/src/main/java/com/lantone/userauth/exception/Oauth2ExceptionHandler.java
  18. 18 0
      user-auth/src/main/java/com/lantone/userauth/service/UmsAdminService.java
  19. 57 0
      user-auth/src/main/java/com/lantone/userauth/service/impl/UserServiceImpl.java
  20. 35 0
      user-auth/src/main/resources/bootstrap.yml
  21. BIN
      user-auth/src/main/resources/jwt.jks
  22. 182 0
      user-auth/src/main/resources/logback-spring.xml

+ 4 - 0
common/pom.xml

@@ -14,6 +14,10 @@
     <packaging>jar</packaging>
 
     <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>

+ 35 - 0
common/src/main/java/com/lantone/common/exception/ApiException.java

@@ -0,0 +1,35 @@
+package com.lantone.common.exception;
+
+import com.lantone.common.api.IErrorCode;
+
+/**
+ * @Description: 自定义API异常
+ * @author: rengb
+ * @time: 2021/1/5 18:27
+ */
+public class ApiException extends RuntimeException {
+
+    private IErrorCode errorCode;
+
+    public ApiException(IErrorCode errorCode) {
+        super(errorCode.getMessage());
+        this.errorCode = 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;
+    }
+
+}

+ 20 - 0
common/src/main/java/com/lantone/common/exception/Asserts.java

@@ -0,0 +1,20 @@
+package com.lantone.common.exception;
+
+import com.lantone.common.api.IErrorCode;
+
+/**
+ * @Description: 断言处理类,用于抛出各种API异常
+ * @author: rengb
+ * @time: 2021/1/5 18:27
+ */
+public class Asserts {
+
+    public static void fail(String message) {
+        throw new ApiException(message);
+    }
+
+    public static void fail(IErrorCode errorCode) {
+        throw new ApiException(errorCode);
+    }
+
+}

+ 57 - 0
common/src/main/java/com/lantone/common/exception/GlobalExceptionHandler.java

@@ -0,0 +1,57 @@
+package com.lantone.common.exception;
+
+import com.lantone.common.api.CommonResult;
+import org.springframework.validation.BindException;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+/**
+ * @Description: 全局异常处理
+ * @author: rengb
+ * @time: 2021/1/5 18:27
+ */
+@ControllerAdvice
+public class GlobalExceptionHandler {
+
+    @ResponseBody
+    @ExceptionHandler(value = ApiException.class)
+    public CommonResult handle(ApiException e) {
+        if (e.getErrorCode() != null) {
+            return CommonResult.failed(e.getErrorCode());
+        }
+        return CommonResult.failed(e.getMessage());
+    }
+
+    @ResponseBody
+    @ExceptionHandler(value = MethodArgumentNotValidException.class)
+    public CommonResult handleValidException(MethodArgumentNotValidException e) {
+        BindingResult bindingResult = e.getBindingResult();
+        String message = null;
+        if (bindingResult.hasErrors()) {
+            FieldError fieldError = bindingResult.getFieldError();
+            if (fieldError != null) {
+                message = fieldError.getField() + fieldError.getDefaultMessage();
+            }
+        }
+        return CommonResult.validateFailed(message);
+    }
+
+    @ResponseBody
+    @ExceptionHandler(value = BindException.class)
+    public CommonResult handleValidException(BindException e) {
+        BindingResult bindingResult = e.getBindingResult();
+        String message = null;
+        if (bindingResult.hasErrors()) {
+            FieldError fieldError = bindingResult.getFieldError();
+            if (fieldError != null) {
+                message = fieldError.getField() + fieldError.getDefaultMessage();
+            }
+        }
+        return CommonResult.validateFailed(message);
+    }
+
+}

+ 1 - 0
pom.xml

@@ -26,6 +26,7 @@
         <module>monitor-service</module>
         <module>security-center</module>
         <module>dblayer-mbg</module>
+        <module>user-auth</module>
     </modules>
 
     <properties>

+ 33 - 0
user-auth/.gitignore

@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/

+ 68 - 0
user-auth/pom.xml

@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>com.lantone</groupId>
+        <artifactId>emrais</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>user-auth</artifactId>
+    <name>user-auth</name>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.lantone</groupId>
+            <artifactId>common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-oauth2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.nimbusds</groupId>
+            <artifactId>nimbus-jose-jwt</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.github.openfeign</groupId>
+            <artifactId>feign-okhttp</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>com.spotify</groupId>
+                <artifactId>docker-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 17 - 0
user-auth/src/main/java/com/lantone/UserAuthApplication.java

@@ -0,0 +1,17 @@
+package com.lantone;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+
+@EnableFeignClients
+@EnableEurekaClient
+@SpringBootApplication
+public class UserAuthApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(UserAuthApplication.class, args);
+    }
+
+}

+ 32 - 0
user-auth/src/main/java/com/lantone/userauth/component/JwtTokenEnhancer.java

@@ -0,0 +1,32 @@
+package com.lantone.userauth.component;
+
+import com.lantone.userauth.domain.SecurityUser;
+import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.security.oauth2.provider.token.TokenEnhancer;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @Description: JWT内容增强器
+ * @author: rengb
+ * @time: 2021/1/5 18:27
+ */
+@Component
+public class JwtTokenEnhancer implements TokenEnhancer {
+
+    @Override
+    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
+        SecurityUser securityUser = (SecurityUser) authentication.getPrincipal();
+        Map<String, Object> info = new HashMap<>();
+        //把用户ID设置到JWT中
+        info.put("id", securityUser.getId());
+        info.put("client_id", securityUser.getClientId());
+        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);
+        return accessToken;
+    }
+
+}

+ 90 - 0
user-auth/src/main/java/com/lantone/userauth/config/Oauth2ServerConfig.java

@@ -0,0 +1,90 @@
+package com.lantone.userauth.config;
+
+import com.lantone.userauth.component.JwtTokenEnhancer;
+import com.lantone.userauth.service.impl.UserServiceImpl;
+import lombok.AllArgsConstructor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
+import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
+import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
+import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
+import org.springframework.security.oauth2.provider.token.TokenEnhancer;
+import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
+import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
+import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
+
+import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @Description: 认证服务器配置
+ * @author: rengb
+ * @time: 2021/1/5 18:27
+ */
+@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 {
+        clients.inMemory()
+                .withClient("admin-app")
+                .secret(passwordEncoder.encode("123456"))
+                .scopes("all")
+                .authorizedGrantTypes("password", "refresh_token")
+                .accessTokenValiditySeconds(3600 * 24)
+                .refreshTokenValiditySeconds(3600 * 24 * 7)
+                .and()
+                .withClient("portal-app")
+                .secret(passwordEncoder.encode("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<>();
+        delegates.add(jwtTokenEnhancer);
+        delegates.add(accessTokenConverter());
+        enhancerChain.setTokenEnhancers(delegates); //配置JWT的内容增强器
+        endpoints.authenticationManager(authenticationManager)
+                .userDetailsService(userDetailsService) //配置加载用户信息的服务
+                .accessTokenConverter(accessTokenConverter())
+                .tokenEnhancer(enhancerChain);
+    }
+
+    @Override
+    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
+        security.allowFormAuthenticationForClients();
+    }
+
+    @Bean
+    public JwtAccessTokenConverter accessTokenConverter() {
+        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
+        jwtAccessTokenConverter.setKeyPair(keyPair());
+        return jwtAccessTokenConverter;
+    }
+
+    @Bean
+    public KeyPair keyPair() {
+        //从classpath下的证书中获取秘钥对
+        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "123456".toCharArray());
+        return keyStoreKeyFactory.getKeyPair("jwt", "123456".toCharArray());
+    }
+
+}

+ 42 - 0
user-auth/src/main/java/com/lantone/userauth/config/WebSecurityConfig.java

@@ -0,0 +1,42 @@
+package com.lantone.userauth.config;
+
+import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+/**
+ * @Description: SpringSecurity配置
+ * @author: rengb
+ * @time: 2021/1/5 18:27
+ */
+@Configuration
+@EnableWebSecurity
+public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        http.authorizeRequests()
+                .requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
+                .antMatchers("/rsa/publicKey").permitAll()
+                .antMatchers("/v2/api-docs").permitAll()
+                .anyRequest().authenticated();
+    }
+
+    @Bean
+    @Override
+    public AuthenticationManager authenticationManagerBean() throws Exception {
+        return super.authenticationManagerBean();
+    }
+
+    @Bean
+    public PasswordEncoder passwordEncoder() {
+        return new BCryptPasswordEncoder();
+    }
+
+}

+ 24 - 0
user-auth/src/main/java/com/lantone/userauth/constant/MessageConstant.java

@@ -0,0 +1,24 @@
+package com.lantone.userauth.constant;
+
+/**
+ * @Description: 消息常量
+ * @author: rengb
+ * @time: 2021/1/5 18:27
+ */
+public class MessageConstant {
+
+    public static final String LOGIN_SUCCESS = "登录成功!";
+
+    public static final String USERNAME_PASSWORD_ERROR = "用户名或密码错误!";
+
+    public static final String CREDENTIALS_EXPIRED = "该账户的登录凭证已过期,请重新登录!";
+
+    public static final String ACCOUNT_DISABLED = "该账户已被禁用,请联系管理员!";
+
+    public static final String ACCOUNT_LOCKED = "该账号已被锁定,请联系管理员!";
+
+    public static final String ACCOUNT_EXPIRED = "该账号已过期,请联系管理员!";
+
+    public static final String PERMISSION_DENIED = "没有访问权限,请联系管理员!";
+
+}

+ 57 - 0
user-auth/src/main/java/com/lantone/userauth/controller/AuthController.java

@@ -0,0 +1,57 @@
+package com.lantone.userauth.controller;
+
+import com.lantone.common.api.CommonResult;
+import com.lantone.common.constant.AuthConstant;
+import com.lantone.userauth.domain.Oauth2TokenDto;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import springfox.documentation.annotations.ApiIgnore;
+
+import java.security.Principal;
+import java.util.Map;
+
+/**
+ * @Description: 自定义Oauth2获取令牌接口
+ * @author: rengb
+ * @time: 2021/1/5 18:27
+ */
+@RestController
+@Api(tags = "AuthController", description = "认证中心登录认证")
+@RequestMapping("/oauth")
+public class AuthController {
+
+    @Autowired
+    private TokenEndpoint tokenEndpoint;
+
+    @ApiOperation("Oauth2获取token")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "grant_type", value = "授权模式", required = true),
+            @ApiImplicitParam(name = "client_id", value = "Oauth2客户端ID", required = true),
+            @ApiImplicitParam(name = "client_secret", value = "Oauth2客户端秘钥", required = true),
+            @ApiImplicitParam(name = "refresh_token", value = "刷新token"),
+            @ApiImplicitParam(name = "username", value = "登录用户名"),
+            @ApiImplicitParam(name = "password", value = "登录密码")
+    })
+    @RequestMapping(value = "/token", method = RequestMethod.POST)
+    public CommonResult<Oauth2TokenDto> postAccessToken(@ApiIgnore Principal principal, @ApiIgnore @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
+        OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
+        Oauth2TokenDto oauth2TokenDto = Oauth2TokenDto.builder()
+                .token(oAuth2AccessToken.getValue())
+                .refreshToken(oAuth2AccessToken.getRefreshToken().getValue())
+                .expiresIn(oAuth2AccessToken.getExpiresIn())
+                .tokenHead(AuthConstant.JWT_TOKEN_PREFIX).build();
+
+        return CommonResult.success(oauth2TokenDto);
+    }
+
+}

+ 31 - 0
user-auth/src/main/java/com/lantone/userauth/controller/KeyPairController.java

@@ -0,0 +1,31 @@
+package com.lantone.userauth.controller;
+
+import com.nimbusds.jose.jwk.JWKSet;
+import com.nimbusds.jose.jwk.RSAKey;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.security.KeyPair;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Map;
+
+/**
+ * @Description: 获取RSA公钥接口
+ * @author: rengb
+ * @time: 2021/1/5 18:27
+ */
+@RestController
+public class KeyPairController {
+
+    @Autowired
+    private KeyPair keyPair;
+
+    @GetMapping("/rsa/publicKey")
+    public Map<String, Object> getKey() {
+        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
+        RSAKey key = new RSAKey.Builder(publicKey).build();
+        return new JWKSet(key).toJSONObject();
+    }
+
+}

+ 27 - 0
user-auth/src/main/java/com/lantone/userauth/domain/Oauth2TokenDto.java

@@ -0,0 +1,27 @@
+package com.lantone.userauth.domain;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * @Description: Oauth2获取Token返回信息封装
+ * @author: rengb
+ * @time: 2021/1/5 18:27
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Builder
+public class Oauth2TokenDto {
+
+    @ApiModelProperty("访问令牌")
+    private String token;
+    @ApiModelProperty("刷令牌")
+    private String refreshToken;
+    @ApiModelProperty("访问令牌头前缀")
+    private String tokenHead;
+    @ApiModelProperty("有效时间(秒)")
+    private int expiresIn;
+
+}

+ 96 - 0
user-auth/src/main/java/com/lantone/userauth/domain/SecurityUser.java

@@ -0,0 +1,96 @@
+package com.lantone.userauth.domain;
+
+import com.lantone.common.domain.UserDto;
+import lombok.Data;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * @Description: 登录用户信息
+ * @author: rengb
+ * @time: 2021/1/5 18:27
+ */
+@Data
+public class SecurityUser implements UserDetails {
+
+    /**
+     * ID
+     */
+    private Long id;
+    /**
+     * 用户名
+     */
+    private String username;
+    /**
+     * 用户密码
+     */
+    private String password;
+    /**
+     * 用户状态
+     */
+    private Boolean enabled;
+    /**
+     * 登录客户端ID
+     */
+    private String clientId;
+    /**
+     * 权限数据
+     */
+    private Collection<SimpleGrantedAuthority> authorities;
+
+    public SecurityUser() {
+
+    }
+
+    public SecurityUser(UserDto userDto) {
+        this.setId(userDto.getId());
+        this.setUsername(userDto.getUsername());
+        this.setPassword(userDto.getPassword());
+        this.setEnabled(userDto.getStatus() == 1);
+        this.setClientId(userDto.getClientId());
+        if (userDto.getRoles() != null) {
+            authorities = new ArrayList<>();
+            userDto.getRoles().forEach(item -> authorities.add(new SimpleGrantedAuthority(item)));
+        }
+    }
+
+    @Override
+    public Collection<? extends GrantedAuthority> getAuthorities() {
+        return this.authorities;
+    }
+
+    @Override
+    public String getPassword() {
+        return this.password;
+    }
+
+    @Override
+    public String getUsername() {
+        return this.username;
+    }
+
+    @Override
+    public boolean isAccountNonExpired() {
+        return true;
+    }
+
+    @Override
+    public boolean isAccountNonLocked() {
+        return true;
+    }
+
+    @Override
+    public boolean isCredentialsNonExpired() {
+        return true;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return this.enabled;
+    }
+
+}

+ 23 - 0
user-auth/src/main/java/com/lantone/userauth/exception/Oauth2ExceptionHandler.java

@@ -0,0 +1,23 @@
+package com.lantone.userauth.exception;
+
+import com.lantone.common.api.CommonResult;
+import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+/**
+ * @Description: 全局处理Oauth2抛出的异常
+ * @author: rengb
+ * @time: 2021/1/5 18:27
+ */
+@ControllerAdvice
+public class Oauth2ExceptionHandler {
+
+    @ResponseBody
+    @ExceptionHandler(value = OAuth2Exception.class)
+    public CommonResult handleOauth2(OAuth2Exception e) {
+        return CommonResult.failed(e.getMessage());
+    }
+
+}

+ 18 - 0
user-auth/src/main/java/com/lantone/userauth/service/UmsAdminService.java

@@ -0,0 +1,18 @@
+package com.lantone.userauth.service;
+
+import com.lantone.common.domain.UserDto;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+/**
+ * @Description:
+ * @author: rengb
+ * @time: 2021/1/5 18:27
+ */
+@FeignClient("security-center")
+public interface UmsAdminService {
+
+    @GetMapping("/admin/loadByUsername")
+    UserDto loadUserByUsername(@RequestParam String username);
+}

+ 57 - 0
user-auth/src/main/java/com/lantone/userauth/service/impl/UserServiceImpl.java

@@ -0,0 +1,57 @@
+package com.lantone.userauth.service.impl;
+
+import com.lantone.common.constant.AuthConstant;
+import com.lantone.common.domain.UserDto;
+import com.lantone.userauth.constant.MessageConstant;
+import com.lantone.userauth.domain.SecurityUser;
+import com.lantone.userauth.service.UmsAdminService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.AccountExpiredException;
+import org.springframework.security.authentication.CredentialsExpiredException;
+import org.springframework.security.authentication.DisabledException;
+import org.springframework.security.authentication.LockedException;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @Description: 用户管理业务类
+ * @author: rengb
+ * @time: 2021/1/5 18:27
+ */
+@Service
+public class UserServiceImpl implements UserDetailsService {
+
+    @Autowired
+    private UmsAdminService adminService;
+    @Autowired
+    private HttpServletRequest request;
+
+    @Override
+    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+        String clientId = request.getParameter("client_id");
+        UserDto userDto = null;
+        if (AuthConstant.ADMIN_CLIENT_ID.equals(clientId)) {
+            userDto = adminService.loadUserByUsername(username);
+        }
+        if (userDto == null) {
+            throw new UsernameNotFoundException(MessageConstant.USERNAME_PASSWORD_ERROR);
+        }
+        userDto.setClientId(clientId);
+        SecurityUser securityUser = new SecurityUser(userDto);
+        if (!securityUser.isEnabled()) {
+            throw new DisabledException(MessageConstant.ACCOUNT_DISABLED);
+        } else if (!securityUser.isAccountNonLocked()) {
+            throw new LockedException(MessageConstant.ACCOUNT_LOCKED);
+        } else if (!securityUser.isAccountNonExpired()) {
+            throw new AccountExpiredException(MessageConstant.ACCOUNT_EXPIRED);
+        } else if (!securityUser.isCredentialsNonExpired()) {
+            throw new CredentialsExpiredException(MessageConstant.CREDENTIALS_EXPIRED);
+        }
+        return securityUser;
+    }
+
+}

+ 35 - 0
user-auth/src/main/resources/bootstrap.yml

@@ -0,0 +1,35 @@
+server:
+  port: 8860
+
+spring:
+  application:
+    name: user-auth
+
+eureka:
+  client:
+    service-url:
+      defaultZone: http://localhost:6751/eureka/
+
+logging:
+  config: classpath:logback-spring.xml
+  file: logs/user-auth/log_info.log
+
+management:
+  endpoints:
+    web:
+      exposure:
+        include: '*'
+  endpoint:
+    health:
+      show-details: always
+
+feign:
+  okhttp:
+    enabled: true
+ribbon:
+  ConnectTimeout: 3000 #服务请求连接超时时间(毫秒)
+  ReadTimeout: 3000 #服务请求处理超时时间(毫秒)
+
+swagger.title: 认证中心
+swagger.des: 认证中心相关接口文档
+swagger.version: 0.0.1-SNAPSHOT

BIN
user-auth/src/main/resources/jwt.jks


+ 182 - 0
user-auth/src/main/resources/logback-spring.xml

@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
+<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
+<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
+<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
+<configuration  scan="true" scanPeriod="10 seconds">
+
+    <!--<include resource="org/springframework/boot/logging/logback/base.xml" />-->
+
+    <contextName>logback</contextName>
+    <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
+    <property name="log.path" value="logs/user-auth" />
+
+    <!-- 彩色日志 -->
+    <!-- 彩色日志依赖的渲染类 -->
+    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
+    <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
+    <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
+    <!-- 彩色日志格式 -->
+    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
+
+
+    <!--输出到控制台-->
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>info</level>
+        </filter>
+        <encoder>
+            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
+            <!-- 设置字符集 -->
+            <charset>UTF-8</charset>
+        </encoder>
+    </appender>
+
+
+    <!--输出到文件-->
+
+    <!-- 时间滚动输出 level为 DEBUG 日志 -->
+    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 正在记录的日志文件的路径及文件名 -->
+        <file>${log.path}/log_debug.log</file>
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+            <charset>UTF-8</charset> <!-- 设置字符集 -->
+        </encoder>
+        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志归档 -->
+            <fileNamePattern>${log.path}/debug/log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>100MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--日志文件保留天数-->
+            <maxHistory>15</maxHistory>
+        </rollingPolicy>
+        <!-- 此日志文件只记录debug级别的 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>debug</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- 时间滚动输出 level为 INFO 日志 -->
+    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 正在记录的日志文件的路径及文件名 -->
+        <file>${log.path}/log_info.log</file>
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 每天日志归档路径以及格式 -->
+            <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>100MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--日志文件保留天数-->
+            <maxHistory>15</maxHistory>
+        </rollingPolicy>
+        <!-- 此日志文件只记录info级别的 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>info</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- 时间滚动输出 level为 WARN 日志 -->
+    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 正在记录的日志文件的路径及文件名 -->
+        <file>${log.path}/log_warn.log</file>
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
+        </encoder>
+        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>100MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--日志文件保留天数-->
+            <maxHistory>15</maxHistory>
+        </rollingPolicy>
+        <!-- 此日志文件只记录warn级别的 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>warn</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+
+    <!-- 时间滚动输出 level为 ERROR 日志 -->
+    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 正在记录的日志文件的路径及文件名 -->
+        <file>${log.path}/log_error.log</file>
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
+        </encoder>
+        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>100MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--日志文件保留天数-->
+            <maxHistory>15</maxHistory>
+        </rollingPolicy>
+        <!-- 此日志文件只记录ERROR级别的 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>ERROR</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!--
+        <logger>用来设置某一个包或者具体的某一个类的日志打印级别、
+        以及指定<appender>。<logger>仅有一个name属性,
+        一个可选的level和一个可选的addtivity属性。
+        name:用来指定受此logger约束的某一个包或者具体的某一个类。
+        level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
+              还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。
+              如果未设置此属性,那么当前logger将会继承上级的级别。
+        addtivity:是否向上级logger传递打印信息。默认是true。
+    -->
+    <!--<logger name="org.springframework.web" level="info"/>-->
+    <!--<logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>-->
+    <!--
+        使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
+        第一种把<root level="info">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息
+        第二种就是单独给dao下目录配置debug模式,代码如下,这样配置sql语句会打印,其他还是正常info级别:
+     -->
+
+
+    <!--
+        root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
+        level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
+        不能设置为INHERITED或者同义词NULL。默认是DEBUG
+        可以包含零个或多个元素,标识这个appender将会添加到这个logger。
+    -->
+
+    <!--开发环境:打印控制台-->
+    <springProfile name="dev">
+        <logger name="org.lantone.qc" level="debug"/>
+    </springProfile>
+
+    <root level="info">
+        <appender-ref ref="CONSOLE"/>
+        <appender-ref ref="INFO_FILE" />
+        <appender-ref ref="ERROR_FILE" />
+    </root>
+</configuration>