初始化提交
This commit is contained in:
21
.gitignore
vendored
Normal file
21
.gitignore
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
target/
|
||||||
|
**/target/
|
||||||
|
pom.xml.tag
|
||||||
|
pom.xml.releaseBackup
|
||||||
|
pom.xml.versionsBackup
|
||||||
|
pom.xml.next
|
||||||
|
release.properties
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
buildNumber.properties
|
||||||
|
.mvn/timing.properties
|
||||||
|
*.iml
|
||||||
|
.idea
|
||||||
|
modules/log
|
||||||
|
modules/logs
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.classpath
|
||||||
|
logs
|
||||||
|
out
|
||||||
|
application.yml
|
||||||
|
bootstrap.yml
|
||||||
16
auth/authentication-client/.gitignore
vendored
Normal file
16
auth/authentication-client/.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
target/
|
||||||
|
logs/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
83
auth/authentication-client/pom.xml
Normal file
83
auth/authentication-client/pom.xml
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>business.chaoran</groupId>
|
||||||
|
<artifactId>authentication-client</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>authentication-client</name>
|
||||||
|
<description>Demo Oauth2 project for Spring Cloud Oauth2 Authentication Client</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>spring-milestones</id>
|
||||||
|
<name>Spring Milestones</name>
|
||||||
|
<url>https://repo.spring.io/libs-milestone</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-openfeign</artifactId>
|
||||||
|
<version>2.0.0.RC2</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>business.chaoran</groupId>
|
||||||
|
<artifactId>core</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</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>
|
||||||
|
<version>RELEASE</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<!--oauth2认证-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt</artifactId>
|
||||||
|
<version>0.9.1</version>
|
||||||
|
</dependency>
|
||||||
|
<!--测试-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.12</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-core</artifactId>
|
||||||
|
<version>1.10.19</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package com.springboot.cloud.auth.client.config;
|
||||||
|
|
||||||
|
import feign.Feign;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
|
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@AutoConfigureBefore(FeignAutoConfiguration.class)
|
||||||
|
@Configuration
|
||||||
|
@ConditionalOnClass(Feign.class)
|
||||||
|
/****
|
||||||
|
* 需要修改成OKHTTP的客户端,需要在配置文件增加
|
||||||
|
* feign.httpclient.enabled=false
|
||||||
|
feign.okhttp.enabled=true
|
||||||
|
*/
|
||||||
|
public class FeignOkHttpConfig {
|
||||||
|
|
||||||
|
private int feignOkHttpReadTimeout = 60;
|
||||||
|
private int feignConnectTimeout = 60;
|
||||||
|
private int feignWriteTimeout = 120;
|
||||||
|
|
||||||
|
//如果此处定义拦截器,则服务之间相互调用控制不了,此模块仅用于远程授权
|
||||||
|
@Bean
|
||||||
|
public okhttp3.OkHttpClient okHttpClient() {
|
||||||
|
return new okhttp3.OkHttpClient.Builder()
|
||||||
|
.readTimeout(feignOkHttpReadTimeout, TimeUnit.SECONDS)
|
||||||
|
.connectTimeout(feignConnectTimeout, TimeUnit.SECONDS)
|
||||||
|
.writeTimeout(feignWriteTimeout, TimeUnit.SECONDS)
|
||||||
|
// .connectionPool(new ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit)) //自定义链接池
|
||||||
|
// .addInterceptor(XXXXXXXInterceptor) //自定义拦截器
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
package com.springboot.cloud.auth.client.provider;
|
||||||
|
|
||||||
|
import com.springboot.cloud.common.core.entity.vo.Result;
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestHeader;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@FeignClient(name = "authentication-server", fallback = AuthProvider.AuthProviderFallback.class, path = "authentication-server")
|
||||||
|
//@FeignClient(name = "authentication-server", fallback = AuthProvider.AuthProviderFallback.class)
|
||||||
|
public interface AuthProvider {
|
||||||
|
/**
|
||||||
|
* 调用签权服务,判断用户是否有权限
|
||||||
|
*
|
||||||
|
* @param authentication
|
||||||
|
* @param url
|
||||||
|
* @param companyId
|
||||||
|
* @return <pre>
|
||||||
|
* Result:
|
||||||
|
* {
|
||||||
|
* code:"000000"
|
||||||
|
* mesg:"请求成功"
|
||||||
|
* data: true/false
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
@PostMapping(value = "/auth/permission")
|
||||||
|
Result auth(@RequestParam("companyId") String companyId, @RequestHeader(HttpHeaders.AUTHORIZATION) String authentication, @RequestParam("url") String url);
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class AuthProviderFallback implements AuthProvider {
|
||||||
|
/**
|
||||||
|
* 降级统一返回无权限
|
||||||
|
*
|
||||||
|
* @param authentication
|
||||||
|
* @param url
|
||||||
|
* @param companyId
|
||||||
|
* @return <pre>
|
||||||
|
* Result:
|
||||||
|
* {
|
||||||
|
* code:"-1"
|
||||||
|
* mesg:"系统异常"
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Result auth(String authentication, String url, String companyId) {
|
||||||
|
return Result.fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
package com.springboot.cloud.auth.client.service;
|
||||||
|
|
||||||
|
import com.springboot.cloud.common.core.entity.vo.Result;
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import io.jsonwebtoken.Jws;
|
||||||
|
|
||||||
|
public interface IAuthService {
|
||||||
|
/**
|
||||||
|
* 调用签权服务,判断用户是否有权限
|
||||||
|
*
|
||||||
|
* @param authentication
|
||||||
|
* @param url
|
||||||
|
* @return Result
|
||||||
|
*/
|
||||||
|
Result authenticate(String companyId,String authentication, String url);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断url是否在忽略的范围内
|
||||||
|
* 只要是配置中的开头,即返回true
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean ignoreAuthentication(String url);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看签权服务器返回结果,有权限返回true
|
||||||
|
*
|
||||||
|
* @param authResult
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean hasPermission(Result authResult);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用签权服务,判断用户是否有权限
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param companyId
|
||||||
|
* @param authentication
|
||||||
|
* @param url
|
||||||
|
* @return true/false
|
||||||
|
*/
|
||||||
|
boolean hasPermission(String companyId,String authentication, String url);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否无效authentication
|
||||||
|
*
|
||||||
|
* @param authentication
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean invalidJwtAccessToken(String authentication);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从认证信息中提取jwt token 对象
|
||||||
|
*
|
||||||
|
* @param jwtToken toke信息 header.payload.signature
|
||||||
|
* @return Jws对象
|
||||||
|
*/
|
||||||
|
Jws<Claims> getJwt(String jwtToken);
|
||||||
|
}
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
package com.springboot.cloud.auth.client.service.impl;
|
||||||
|
|
||||||
|
import com.springboot.cloud.auth.client.provider.AuthProvider;
|
||||||
|
import com.springboot.cloud.auth.client.service.IAuthService;
|
||||||
|
import com.springboot.cloud.common.core.entity.vo.Result;
|
||||||
|
import io.jsonwebtoken.*;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class AuthService implements IAuthService {
|
||||||
|
/**
|
||||||
|
* Authorization认证开头是"bearer "
|
||||||
|
*/
|
||||||
|
private static final String BEARER = "Bearer ";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthProvider authProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* jwt token 密钥,主要用于token解析,签名验证
|
||||||
|
*/
|
||||||
|
@Value("${spring.security.oauth2.jwt.signingKey}")
|
||||||
|
private String signingKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不需要网关签权的url配置(/oauth,/open)
|
||||||
|
* 默认/oauth开头是不需要的
|
||||||
|
*/
|
||||||
|
@Value("${gate.ignore.authentication.startWith}")
|
||||||
|
private String ignoreUrls = "/oauth";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result authenticate(String companyId,String authentication, String url) {
|
||||||
|
return authProvider.auth(companyId,authentication, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean ignoreAuthentication(String url) {
|
||||||
|
return Stream.of(this.ignoreUrls.split(",")).anyMatch(ignoreUrl -> url.startsWith(StringUtils.trim(ignoreUrl)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(Result authResult) {
|
||||||
|
log.debug("签权结果:{}", authResult.getData());
|
||||||
|
return authResult.isSuccess() && (boolean) authResult.getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(String companyId, String authentication, String url) {
|
||||||
|
// 如果请求未携带token信息, 直接权限
|
||||||
|
if (StringUtils.isBlank(authentication) || !authentication.startsWith(BEARER)) {
|
||||||
|
log.error("user token is null");
|
||||||
|
return Boolean.FALSE;
|
||||||
|
}
|
||||||
|
//token是否有效,在网关进行校验,无效/过期等
|
||||||
|
if (invalidJwtAccessToken(authentication)) {
|
||||||
|
return Boolean.FALSE;
|
||||||
|
}
|
||||||
|
//从认证服务获取是否有权限,远程调用
|
||||||
|
return hasPermission(authenticate(companyId,authentication, url));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Jws<Claims> getJwt(String jwtToken) {
|
||||||
|
if (jwtToken.startsWith(BEARER)) {
|
||||||
|
jwtToken = StringUtils.substring(jwtToken, BEARER.length());
|
||||||
|
}
|
||||||
|
return Jwts.parser() //得到DefaultJwtParser
|
||||||
|
.setSigningKey(signingKey.getBytes()) //设置签名的秘钥
|
||||||
|
.parseClaimsJws(jwtToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean invalidJwtAccessToken(String authentication) {
|
||||||
|
// 是否无效true表示无效
|
||||||
|
boolean invalid = Boolean.TRUE;
|
||||||
|
try {
|
||||||
|
getJwt(authentication);
|
||||||
|
invalid = Boolean.FALSE;
|
||||||
|
} catch (SignatureException | ExpiredJwtException | MalformedJwtException ex) {
|
||||||
|
log.error("user token error :{}", ex.getMessage());
|
||||||
|
}
|
||||||
|
return invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,111 @@
|
|||||||
|
package com.springboot.cloud.auth.client.service.impl;
|
||||||
|
|
||||||
|
import com.springboot.cloud.auth.client.provider.AuthProvider;
|
||||||
|
import com.springboot.cloud.auth.client.service.IAuthService;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
public class AuthServiceTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
IAuthService authService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
AuthProvider authProvider;
|
||||||
|
|
||||||
|
private static final String VALID_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbInJlYWQiXSwib3JnYW5pemF0aW9uIjoiYWRtaW4iLCJleHAiOjEzNTcyMDIxNjM3LCJhdXRob3JpdGllcyI6WyJBRE1JTiJdLCJqdGkiOiI4MzcyMzI0Ny00ZDA5LTQ0YjYtYTNlOS01OGUzYzZiMGUzYjIiLCJjbGllbnRfaWQiOiJ0ZXN0X2NsaWVudCJ9.IkOtKapS5PJLKU1NfiqVSCgsQngE7qGnIx1NziJMvVA";
|
||||||
|
private static final String BEARER = "Bearer ";
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
authService = new AuthService();
|
||||||
|
setInstancePrivateField(authService, "signingKey", "123456");
|
||||||
|
setInstancePrivateField(authService, "ignoreUrls", "/oauth,/open");
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setInstancePrivateField(Object instance, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
Field signingKeyField = instance.getClass().getDeclaredField(fieldName);
|
||||||
|
signingKeyField.setAccessible(true);
|
||||||
|
signingKeyField.set(instance, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidJwtAccessToken_假如授权服务通过给定密钥生成了token_当输入该token组成的authentication_那么返回false表示token有效() {
|
||||||
|
Assert.assertFalse(authService.invalidJwtAccessToken(BEARER + VALID_TOKEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidJwtAccessToken_假如_当输入随机字串_那么返回true表示token无效() {
|
||||||
|
String authentication = BEARER + "im random string";
|
||||||
|
Assert.assertTrue(authService.invalidJwtAccessToken(authentication));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidJwtAccessToken_假如有人获取用户token_当输入篡改过playload中信息_那么返回true表示token无效() {
|
||||||
|
String authentication = BEARER + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.111eyJGc2VyX25hbWUiOiJ6aG91dGFvbyIsInNjb3BlIjpbInJlYWQiXSwib3JnYW5pemF0aW9uIjoiemhvdXRhb28iLCJleHAiOjE1Mjc0NTM5NDQsImF1dGhvcml0aWVzIjpbIkFETUlOIiwiSVQiXSwianRpIjoiZTZiNzM5ZmUtYWEzZC00Y2RmLWIxZjUtNzZkMmVlMjU0ODU1IiwiY2xpZW50X2lkIjoidGVzdF9jbGllbnQifQ.l6PQrs98zT40H6Ad4NHE7NSXyeWnMn-ZhURw3zO-EfE";
|
||||||
|
Assert.assertTrue(authService.invalidJwtAccessToken(authentication));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidJwtAccessToken_假如有人获取用户token_当输入token去掉了signature_那么返回true表示token无效() {
|
||||||
|
String authentication = BEARER + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ6aG91dGFvbyIsInNjb3BlIjpbInJlYWQiXSwib3JnYW5pemF0aW9uIjoiemhvdXRhb28iLCJleHAiOjE1Mjc0NTM5NDQsImF1dGhvcml0aWVzIjpbIkFETUlOIiwiSVQiXSwianRpIjoiZTZiNzM5ZmUtYWEzZC00Y2RmLWIxZjUtNzZkMmVlMjU0ODU1IiwiY2xpZW50X2lkIjoidGVzdF9jbGllbnQifQ";
|
||||||
|
Assert.assertTrue(authService.invalidJwtAccessToken(authentication));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAuthenticate_假如用户authentication正确且有对请求url有权限_当用户请求该url_那么返回成功有权限() {
|
||||||
|
// when(authProvider.auth(BEARER + VALID_TOKEN, "/users", , "POST")).thenReturn(Result.success(true));
|
||||||
|
Assert.assertTrue((Boolean) authService.authenticate(BEARER + VALID_TOKEN, "/users", "POST").getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAuthenticate_假如用户authentication正确且有对请求url只有POST权限_当用户请求该url的GET_那么返回成功无权限() {
|
||||||
|
// when(authProvider.auth(BEARER + VALID_TOKEN, "/users", , "GET")).thenReturn(Result.success(false));
|
||||||
|
Assert.assertFalse((Boolean) authService.authenticate(BEARER + VALID_TOKEN, "/users", "GET").getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHasPermission_假如_当传入无效token_那么返回无权限() {
|
||||||
|
// Assert.assertFalse(authService.hasPermission(, "invalid token", "/users"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHasPermission_假如用户authentication正确且有对请求url有权限_当用户请求该url_那么返回成功有权限() {
|
||||||
|
// when(authProvider.auth(BEARER + VALID_TOKEN, "/users", , "POST")).thenReturn(Result.success(true));
|
||||||
|
// Assert.assertTrue(authService.hasPermission(, BEARER + VALID_TOKEN, "/users"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHasPermission_假如用户authentication正确且有对请求url只有POST权限_当用户请求该url的GET_那么返回成功无权限() {
|
||||||
|
// when(authProvider.auth(BEARER + VALID_TOKEN, "/users", , "GET")).thenReturn(Result.success(false));
|
||||||
|
// Assert.assertFalse(authService.hasPermission(, BEARER + VALID_TOKEN, "/users"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIgnoreAuthentication_假如配置的忽略前缀为oauth和open_当用户请求以oauth开头的url_那么返回返回true() {
|
||||||
|
Assert.assertTrue(authService.ignoreAuthentication("/oauth/token?test=123"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIgnoreAuthentication_假如配置的忽略前缀为oauth和open_当用户请求以open开头的url_那么返回返回true() {
|
||||||
|
Assert.assertTrue(authService.ignoreAuthentication("/open/"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIgnoreAuthentication_假如配置的忽略前缀为oauth和open_当用户请求以test开头的url_那么返回返回true() {
|
||||||
|
Assert.assertFalse(authService.ignoreAuthentication("/test"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIgnoreAuthentication_假如配置的忽略前缀为oauth和open_当用户请求以open结尾的url_那么返回返回true() {
|
||||||
|
Assert.assertFalse(authService.ignoreAuthentication("/test/open"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
16
auth/authentication-server/.gitignore
vendored
Normal file
16
auth/authentication-server/.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
target/
|
||||||
|
logs/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
107
auth/authentication-server/pom.xml
Normal file
107
auth/authentication-server/pom.xml
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>business.chaoran</groupId>
|
||||||
|
<artifactId>authentication-server</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<packaging>war</packaging>
|
||||||
|
<!-- <packaging>jar</packaging>-->
|
||||||
|
|
||||||
|
<name>authentication-server</name>
|
||||||
|
<description>Demo Oauth2 project for Spring Cloud Oauth2 Authentication Server</description>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>business.chaoran</groupId>
|
||||||
|
<artifactId>auth</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>business.chaoran</groupId>
|
||||||
|
<artifactId>web</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
<!--oauth2认证-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-security</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!--Swagger2 - RESTful API文档-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.springfox</groupId>
|
||||||
|
<artifactId>springfox-swagger2</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.springfox</groupId>
|
||||||
|
<artifactId>springfox-swagger-ui</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!--jetcache缓存 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alicp.jetcache</groupId>
|
||||||
|
<artifactId>jetcache-starter-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 独立运行依赖-->
|
||||||
|
<!-- <dependency>-->
|
||||||
|
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||||
|
<!-- <artifactId>spring-boot-starter-web</artifactId>-->
|
||||||
|
<!-- </dependency>-->
|
||||||
|
|
||||||
|
|
||||||
|
<!-- tomcat部署运行依赖-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-simple</artifactId>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-logging</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- tomcat容器运行构建工具-->
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<version>2.1.4.RELEASE</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>build-info</goal>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-war-plugin</artifactId>
|
||||||
|
<version>3.0.0</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
<finalName>authentication-server</finalName>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
|
||||||
|
</project>
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication;
|
||||||
|
|
||||||
|
import com.alicp.jetcache.anno.config.EnableCreateCacheAnnotation;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
|
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||||
|
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||||
|
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableDiscoveryClient
|
||||||
|
@EnableFeignClients
|
||||||
|
@EnableCreateCacheAnnotation
|
||||||
|
public class Oauth2AuthenticationApplication extends SpringBootServletInitializer{
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(Oauth2AuthenticationApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
|
||||||
|
return builder.sources(Oauth2AuthenticationApplication.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,104 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
|
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.springboot.cloud.auth.authentication.events.BusReceiver;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.amqp.core.*;
|
||||||
|
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||||
|
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
|
||||||
|
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
|
||||||
|
import org.springframework.amqp.support.converter.ContentTypeDelegatingMessageConverter;
|
||||||
|
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||||
|
import org.springframework.amqp.support.converter.MessageConverter;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息设置
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@Slf4j
|
||||||
|
public class BusConfig {
|
||||||
|
|
||||||
|
private static final String EXCHANGE_NAME = "spring-boot-exchange";
|
||||||
|
private static final String ROUTING_KEY = "organization-popedom";
|
||||||
|
|
||||||
|
@Value("${spring.application.name}")
|
||||||
|
private String appName;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置监听队列,mq只能直接监听队列,不能直接监听交换机
|
||||||
|
* @return 队列
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
Queue queue() {
|
||||||
|
String queueName = new Base64UrlNamingStrategy(appName + ".").generateName();
|
||||||
|
log.info("queue name:{}", queueName);
|
||||||
|
return new Queue(queueName, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 采用主题交换机
|
||||||
|
* @return 主题交换机
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
TopicExchange exchange() {
|
||||||
|
log.info("exchange:{}", EXCHANGE_NAME);
|
||||||
|
return new TopicExchange(EXCHANGE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将队列绑定到主题交换机上,以router_key作为键
|
||||||
|
* @param queue
|
||||||
|
* @param exchange
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
Binding binding(Queue queue, TopicExchange exchange) {
|
||||||
|
log.info("binding {} to {} with {}", queue, exchange, ROUTING_KEY);
|
||||||
|
return BindingBuilder.bind(queue).to(exchange).with(ROUTING_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置消息处理监听器
|
||||||
|
* @param connectionFactory mq连接工厂,用于配置mq服务器地址,端口,连接关闭或者断开监听器等等
|
||||||
|
* @param messageListenerAdapter 消息监听适配器,用于收到消息后对消息进行处理,或者是代理到其他对象进行处理的适配器,默认处理方法为handleMessage
|
||||||
|
* @param queue 消息接收队列,可以配置多个队列
|
||||||
|
* @return 消息监听容器,可以设置消费者数量、最大最小数量、批量消费等等
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
SimpleMessageListenerContainer simpleMessageListenerContainer(ConnectionFactory connectionFactory, MessageListenerAdapter messageListenerAdapter, Queue queue) {
|
||||||
|
log.info("init simpleMessageListenerContainer {}", queue.getName());
|
||||||
|
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
|
||||||
|
container.setQueueNames(queue.getName());
|
||||||
|
container.setMessageListener(messageListenerAdapter);
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置消息监听适配器
|
||||||
|
* @param busReceiver 处理消息的代理对象
|
||||||
|
* @param messageConverter 消息转换器,此处用Jackson作为json转换工具类
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
MessageListenerAdapter messageListenerAdapter(BusReceiver busReceiver, MessageConverter messageConverter) {
|
||||||
|
log.info("new listener");
|
||||||
|
return new MessageListenerAdapter(busReceiver, messageConverter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息转换器
|
||||||
|
* @return Jackson转换器
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public MessageConverter messageConverter() {
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||||
|
return new ContentTypeDelegatingMessageConverter(new Jackson2JsonMessageConverter(objectMapper));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication.config;
|
||||||
|
|
||||||
|
/*
|
||||||
|
**********************************************
|
||||||
|
* DATE PERSON REASON
|
||||||
|
* 2020/12/7 FXY Created
|
||||||
|
**********************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
import com.springboot.cloud.common.web.interceptor.FeignBasicAuthRequestInterceptor;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class InterceptConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public FeignBasicAuthRequestInterceptor interceptor(){
|
||||||
|
return new FeignBasicAuthRequestInterceptor();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication.config;
|
||||||
|
|
||||||
|
import com.springboot.cloud.auth.authentication.service.PopedomService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class LoadResourceDefine {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PopedomService popedomService;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void resourceConfigAttributes() {
|
||||||
|
popedomService.loadPopedom();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication.config;
|
||||||
|
|
||||||
|
/*
|
||||||
|
**********************************************
|
||||||
|
* DATE PERSON REASON
|
||||||
|
* 2020-12-24 FXY Created
|
||||||
|
**********************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
import com.alibaba.cloud.nacos.registry.NacosAutoServiceRegistration;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.ApplicationArguments;
|
||||||
|
import org.springframework.boot.ApplicationRunner;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import javax.management.MBeanServer;
|
||||||
|
import javax.management.ObjectName;
|
||||||
|
import javax.management.Query;
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Slf4j
|
||||||
|
public class NacosRegisterConfig implements ApplicationRunner {
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private NacosAutoServiceRegistration registration;
|
||||||
|
|
||||||
|
private Integer port;
|
||||||
|
|
||||||
|
public NacosRegisterConfig() {
|
||||||
|
try {
|
||||||
|
this.port = Integer.parseInt(getTomcatPort());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取tomcat端口出错了,原因:{}", e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(ApplicationArguments args) {
|
||||||
|
if (registration != null && port != null) {
|
||||||
|
registration.setPort(port);
|
||||||
|
registration.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取tomcat端口
|
||||||
|
private String getTomcatPort() throws Exception {
|
||||||
|
MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
|
||||||
|
Set<ObjectName> objectNames = beanServer.queryNames(new ObjectName("*:type=Connector,*"), Query.match(Query.attr("protocol"), Query.value("HTTP/1.1")));
|
||||||
|
String port = objectNames.iterator().next().getKeyProperty("port");
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication.config;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
|
||||||
|
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
|
||||||
|
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
|
||||||
|
import org.springframework.security.oauth2.provider.token.TokenStore;
|
||||||
|
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
|
||||||
|
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Configuration
|
||||||
|
@EnableResourceServer
|
||||||
|
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
|
||||||
|
|
||||||
|
@Value("${spring.security.oauth2.jwt.signingKey}")
|
||||||
|
private String signingKey;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(ResourceServerSecurityConfigurer resourceServerSecurityConfigurer) {
|
||||||
|
resourceServerSecurityConfigurer
|
||||||
|
.tokenStore(tokenStore())
|
||||||
|
.resourceId("WEBS");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(HttpSecurity http) throws Exception {
|
||||||
|
log.debug("HttpSecurity configure method");
|
||||||
|
http.csrf().disable();
|
||||||
|
http.authorizeRequests()
|
||||||
|
.antMatchers("/actuator/**").permitAll()
|
||||||
|
.antMatchers("/v2/api-docs").permitAll()
|
||||||
|
.anyRequest().authenticated();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public TokenStore tokenStore() {
|
||||||
|
return new JwtTokenStore(accessTokenConverter());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JwtAccessTokenConverter accessTokenConverter() {
|
||||||
|
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
|
||||||
|
converter.setSigningKey(signingKey);
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication.entity;
|
||||||
|
|
||||||
|
/*
|
||||||
|
**********************************************
|
||||||
|
* DATE PERSON REASON
|
||||||
|
* 2020-12-29 FXY Created
|
||||||
|
**********************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class License {
|
||||||
|
private String companyId;
|
||||||
|
private String franchiserId;
|
||||||
|
private String applicationCode;
|
||||||
|
private String grade;
|
||||||
|
private Date expireDate;
|
||||||
|
private boolean isForever;
|
||||||
|
private String state;
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication.entity;
|
||||||
|
|
||||||
|
/*
|
||||||
|
**********************************************
|
||||||
|
* DATE PERSON REASON
|
||||||
|
* 2020-12-29 FXY Created
|
||||||
|
**********************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Popedom {
|
||||||
|
private String id;
|
||||||
|
private String applicationCode;
|
||||||
|
private String name;
|
||||||
|
private String parentId;
|
||||||
|
private String url;
|
||||||
|
private String icon;
|
||||||
|
private String isMenu;
|
||||||
|
private String description;
|
||||||
|
private String path;
|
||||||
|
private String redirect;
|
||||||
|
private String component;
|
||||||
|
private String title;
|
||||||
|
private boolean alwaysShow;
|
||||||
|
private boolean hidden;
|
||||||
|
private String companyId;
|
||||||
|
private Integer orderNo;
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication.events;
|
||||||
|
|
||||||
|
import com.springboot.cloud.auth.authentication.entity.Popedom;
|
||||||
|
import com.springboot.cloud.auth.authentication.service.PopedomService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息接收者,收到消息后进行处理,内部反射调用,默认回调方法handleMessage,方法参数由消息
|
||||||
|
* 发送方和接收方约定,方法参数尽可能作为一个对象,多个参数向上封装成一个对象
|
||||||
|
*
|
||||||
|
* @see MessageListenerAdapter
|
||||||
|
* @see org.springframework.amqp.core.MessageListener
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class BusReceiver {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PopedomService popedomService;
|
||||||
|
|
||||||
|
public void handleMessage(List<Popedom> popedoms) {
|
||||||
|
log.info("Received Message:<{}>", popedoms);
|
||||||
|
popedomService.savePopedom(popedoms);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication.provider;
|
||||||
|
|
||||||
|
import com.springboot.cloud.auth.authentication.entity.License;
|
||||||
|
import com.springboot.cloud.auth.authentication.entity.Popedom;
|
||||||
|
import com.springboot.cloud.common.core.entity.vo.Result;
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@FeignClient(name = "organization", fallback = PopedomProviderFallback.class, path = "organization")
|
||||||
|
//@FeignClient(name = "organization", fallback = PopedomProviderFallback.class)
|
||||||
|
public interface PopedomProvider {
|
||||||
|
|
||||||
|
@GetMapping(value = "/company/getAllPopedom")
|
||||||
|
Result<List<Popedom>> popedoms();
|
||||||
|
|
||||||
|
@GetMapping(value = "/company/getPopedom")
|
||||||
|
Result<List<Popedom>> popedoms(@RequestParam("companyId") String companyId,@RequestParam("username")String username);
|
||||||
|
|
||||||
|
@GetMapping(value = "/company/license")
|
||||||
|
Result<License> license(@RequestParam("companyId")String companyId,@RequestParam("applicationCode")String applicationCode);
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication.provider;
|
||||||
|
|
||||||
|
import com.springboot.cloud.auth.authentication.entity.License;
|
||||||
|
import com.springboot.cloud.auth.authentication.entity.Popedom;
|
||||||
|
import com.springboot.cloud.common.core.entity.vo.Result;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class PopedomProviderFallback implements PopedomProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<List<Popedom>> popedoms() {
|
||||||
|
log.error("认证服务启动时加载资源异常!未加载到资源!");
|
||||||
|
return Result.fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<List<Popedom>> popedoms(String companyId, String username) {
|
||||||
|
log.error("认证服务查询用户异常!查询用户资源为空!");
|
||||||
|
return Result.success(new ArrayList<Popedom>());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<License> license(String companyId, String applicationCode) {
|
||||||
|
log.error("认证服务查询企业应用有效期异常!企业应用过期!");
|
||||||
|
return Result.success(new License());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication.rest;
|
||||||
|
|
||||||
|
import com.springboot.cloud.auth.authentication.service.AuthenticationService;
|
||||||
|
import com.springboot.cloud.common.core.entity.vo.Result;
|
||||||
|
import io.swagger.annotations.*;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@Api("auth")
|
||||||
|
@Slf4j
|
||||||
|
public class AuthenticationController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
AuthenticationService authenticationService;
|
||||||
|
|
||||||
|
@ApiOperation(value = "权限验证", notes = "根据用户token,访问的url和method判断用户是否有权限访问")
|
||||||
|
@ApiImplicitParams({
|
||||||
|
@ApiImplicitParam(paramType = "query", name = "url", value = "访问的url", required = true, dataType = "string"),
|
||||||
|
@ApiImplicitParam(paramType = "query", name = "method", value = "访问的method", required = true, dataType = "string")
|
||||||
|
})
|
||||||
|
@ApiResponses(@ApiResponse(code = 200, message = "处理成功", response = Result.class))
|
||||||
|
@PostMapping(value = "/auth/permission")
|
||||||
|
public Result decide(@RequestParam String url, HttpServletRequest request) {
|
||||||
|
boolean decide = authenticationService.decide(new HttpServletRequestAuthWrapper(request, url));
|
||||||
|
return Result.success(decide);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication.rest;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletRequestWrapper;
|
||||||
|
|
||||||
|
public class HttpServletRequestAuthWrapper extends HttpServletRequestWrapper {
|
||||||
|
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param url
|
||||||
|
*/
|
||||||
|
public HttpServletRequestAuthWrapper(HttpServletRequest request, String url) {
|
||||||
|
super(request);
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getServletPath() {
|
||||||
|
return this.url;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication.service;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public interface AuthenticationService {
|
||||||
|
/**
|
||||||
|
* 校验权限
|
||||||
|
*
|
||||||
|
* @param authRequest
|
||||||
|
* @return 是否有权限
|
||||||
|
*/
|
||||||
|
boolean decide(HttpServletRequest authRequest);
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication.service;
|
||||||
|
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
||||||
|
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class NewMvcRequestMatcher extends MvcRequestMatcher {
|
||||||
|
|
||||||
|
private String pattern;
|
||||||
|
|
||||||
|
public NewMvcRequestMatcher(HandlerMappingIntrospector introspector, String pattern) {
|
||||||
|
super(introspector, pattern);
|
||||||
|
this.pattern = pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
NewMvcRequestMatcher that = (NewMvcRequestMatcher) o;
|
||||||
|
return Objects.equal(pattern, that.pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication.service;
|
||||||
|
|
||||||
|
import com.springboot.cloud.auth.authentication.entity.Popedom;
|
||||||
|
import org.springframework.security.access.ConfigAttribute;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public interface PopedomService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态新增更新权限
|
||||||
|
*
|
||||||
|
* @param popedoms
|
||||||
|
*/
|
||||||
|
void savePopedom(List<Popedom> popedoms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态删除权限
|
||||||
|
*
|
||||||
|
* @param popedom
|
||||||
|
*/
|
||||||
|
void removePopedom(Popedom popedom);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载权限资源数据
|
||||||
|
*/
|
||||||
|
void loadPopedom();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据url和method查询到对应的权限信息
|
||||||
|
*
|
||||||
|
* @param authRequest
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<ConfigAttribute> findConfigAttributesByUrl(HttpServletRequest authRequest);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户名查询 该用户所拥有的角色对应的资源信息
|
||||||
|
* @param companyId
|
||||||
|
* @param username
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<Popedom> queryByUsername(String companyId,String username);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源对应的应用是否过期
|
||||||
|
*/
|
||||||
|
boolean license(String companyId, String applicationCode);
|
||||||
|
}
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication.service.impl;
|
||||||
|
|
||||||
|
import com.springboot.cloud.auth.authentication.entity.Popedom;
|
||||||
|
import com.springboot.cloud.auth.authentication.service.AuthenticationService;
|
||||||
|
import com.springboot.cloud.auth.authentication.service.PopedomService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.access.ConfigAttribute;
|
||||||
|
import org.springframework.security.access.SecurityConfig;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class AuthenticationServiceImpl implements AuthenticationService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PopedomService popedomService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param authRequest 访问的url,method
|
||||||
|
* @return 有权限true, 无权限或全局资源中未找到请求url返回否
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean decide(HttpServletRequest authRequest) {
|
||||||
|
log.debug("正在访问的url是:{}", authRequest.getServletPath());
|
||||||
|
//获取用户认证信息
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
//获取此url访问对应的权限资源信息
|
||||||
|
List<ConfigAttribute> configAttributes = popedomService.findConfigAttributesByUrl(authRequest);
|
||||||
|
if (configAttributes.size() == 0) {
|
||||||
|
//如果未匹配到资源,则返回未授权
|
||||||
|
return false;
|
||||||
|
} else if (configAttributes.size() == 1) {
|
||||||
|
//默认授权所有资源,所有资源都会匹配,如果仅匹配到一个资源,则必定不是功能接口
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
String companyId = Optional.ofNullable(authRequest.getParameter("companyId")).orElseGet(String::new);
|
||||||
|
// String companyId = Optional.ofNullable(UserContextHolder.getInstance().getCurrentCompany()).orElseGet(String::new);
|
||||||
|
//获取此访问用户所有角色拥有的权限资源
|
||||||
|
List<Popedom> userPopedoms = findPopedomByUsername(companyId, authentication.getName());
|
||||||
|
//用户拥有权限资源 与 url要求的资源进行对比
|
||||||
|
return isMatch(companyId,configAttributes, userPopedoms);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* url对应资源与用户拥有资源进行匹配
|
||||||
|
* 前端传入所在公司
|
||||||
|
* @param userPopedoms
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isMatch(String companyId,List<ConfigAttribute> urlConfigAttributes, List<Popedom> userPopedoms) {
|
||||||
|
//首先检查用户所属角色是否有资源权限
|
||||||
|
Optional<Popedom> optionalPopedom = userPopedoms.stream().filter(popedom -> urlConfigAttributes.contains(new SecurityConfig(popedom.getId()))).findAny();
|
||||||
|
if(optionalPopedom.isPresent()){
|
||||||
|
//再检查用户所在公司应用是否过期
|
||||||
|
return popedomService.license(companyId, optionalPopedom.orElseGet(Popedom::new).getApplicationCode());
|
||||||
|
}else{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户所被授予的角色,查询到用户所拥有的资源
|
||||||
|
*
|
||||||
|
* @param username
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private List<Popedom> findPopedomByUsername(String companyId, String username) {
|
||||||
|
//用户被授予的角色资源
|
||||||
|
List<Popedom> popedoms = popedomService.queryByUsername(companyId, username);
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("用户被授予角色的资源数量是:{}, 资源集合信息为:{}", popedoms.size(), popedoms);
|
||||||
|
}
|
||||||
|
return popedoms;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,127 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication.service.impl;
|
||||||
|
|
||||||
|
import com.springboot.cloud.auth.authentication.entity.License;
|
||||||
|
import com.springboot.cloud.auth.authentication.entity.Popedom;
|
||||||
|
import com.springboot.cloud.auth.authentication.provider.PopedomProvider;
|
||||||
|
import com.springboot.cloud.auth.authentication.service.NewMvcRequestMatcher;
|
||||||
|
import com.springboot.cloud.auth.authentication.service.PopedomService;
|
||||||
|
import com.springboot.cloud.common.core.entity.vo.Result;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.access.ConfigAttribute;
|
||||||
|
import org.springframework.security.access.SecurityConfig;
|
||||||
|
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
||||||
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class PopedomServiceImpl implements PopedomService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private HandlerMappingIntrospector mvcHandlerMappingIntrospector;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PopedomProvider popedomProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统中权限分组集合
|
||||||
|
* key:companyId,value:{key:url,value:config}
|
||||||
|
*/
|
||||||
|
private static final Map<String, Map<RequestMatcher, ConfigAttribute>> popedomConfigAttributes = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void savePopedom(List<Popedom> popedoms) {
|
||||||
|
popedoms.stream().forEach(popedom -> {
|
||||||
|
if (popedomConfigAttributes.containsKey(popedom.getCompanyId())) {
|
||||||
|
//如果包含company对应的权限表,则更新
|
||||||
|
Map<RequestMatcher, ConfigAttribute> attributeMap = popedomConfigAttributes.get(popedom.getCompanyId());
|
||||||
|
attributeMap.put(this.newMvcRequestMatcher(popedom.getUrl()), new SecurityConfig(popedom.getId()));
|
||||||
|
} else {
|
||||||
|
//如果不包含company对应的权限表,则新增
|
||||||
|
Map<RequestMatcher, ConfigAttribute> map = new HashMap<>();
|
||||||
|
map.put(this.newMvcRequestMatcher(popedom.getUrl()), new SecurityConfig(popedom.getId()));
|
||||||
|
popedomConfigAttributes.put(popedom.getCompanyId(), map);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
log.info("popedomConfigAttributes size:{}", popedomConfigAttributes.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void removePopedom(Popedom popedom) {
|
||||||
|
if (popedomConfigAttributes.containsKey(popedom.getCompanyId())) {
|
||||||
|
Map<RequestMatcher, ConfigAttribute> attributeMap = popedomConfigAttributes.get(popedom.getCompanyId());
|
||||||
|
attributeMap.remove(this.newMvcRequestMatcher(popedom.getUrl()));
|
||||||
|
}
|
||||||
|
log.info("resourceConfigAttributes size:{}", popedomConfigAttributes.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void loadPopedom() {
|
||||||
|
Result<List<Popedom>> resourcesResult = popedomProvider.popedoms();
|
||||||
|
if (resourcesResult.isFail()) {
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
List<Popedom> popedoms = resourcesResult.getData();
|
||||||
|
//先根据公司分组,然后放入内存
|
||||||
|
Map<String, Map<RequestMatcher, ConfigAttribute>> map = popedoms.stream().filter(a -> null != a.getUrl() && !a.getUrl().equals("")).collect(Collectors.groupingBy(Popedom::getCompanyId, Collectors.toMap(a -> newMvcRequestMatcher(a.getUrl()), a -> new SecurityConfig(a.getId()))));
|
||||||
|
popedomConfigAttributes.putAll(map);
|
||||||
|
log.debug("init resourceConfigAttributes:{}", popedomConfigAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ConfigAttribute> findConfigAttributesByUrl(HttpServletRequest authRequest) {
|
||||||
|
String companyId = Optional.ofNullable(authRequest.getParameter("companyId")).orElseGet(String::new);
|
||||||
|
// String companyId = Optional.ofNullable(UserContextHolder.getInstance().getCurrentCompany()).orElseGet(String::new);
|
||||||
|
if (popedomConfigAttributes.containsKey(companyId)) {
|
||||||
|
Map<RequestMatcher, ConfigAttribute> attributeMap = popedomConfigAttributes.get(companyId);
|
||||||
|
return attributeMap.keySet().stream()
|
||||||
|
.filter(requestMatcher -> requestMatcher.matches(authRequest))
|
||||||
|
.map(requestMatcher -> attributeMap.get(requestMatcher))
|
||||||
|
.peek(urlConfigAttribute -> log.debug("url在资源池中配置:{}", urlConfigAttribute.getAttribute()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
} else {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Popedom> queryByUsername(String companyId, String username) {
|
||||||
|
return popedomProvider.popedoms(companyId, username).getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean license(String companyId, String applicationCode) {
|
||||||
|
Result<License> licenseResult = popedomProvider.license(companyId, applicationCode);
|
||||||
|
if (licenseResult.isFail()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
License data = licenseResult.getData();
|
||||||
|
if (null == data) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (data.isForever()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (data.getExpireDate().getTime() > new Date().getTime()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建RequestMatcher
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private MvcRequestMatcher newMvcRequestMatcher(String url) {
|
||||||
|
return new NewMvcRequestMatcher(mvcHandlerMappingIntrospector, url);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
|
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
||||||
|
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
|
public class ApplicationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMethod() {
|
||||||
|
List<SimpleGrantedAuthority> authorities;
|
||||||
|
SimpleGrantedAuthority admin = new SimpleGrantedAuthority("ADMIN");
|
||||||
|
SimpleGrantedAuthority user = new SimpleGrantedAuthority("USER");
|
||||||
|
authorities = Lists.newArrayList(admin, user);
|
||||||
|
authorities.stream().map(authority -> authority.getAuthority()).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMethod1() throws JsonProcessingException {
|
||||||
|
|
||||||
|
// Resource resource = new Resource();
|
||||||
|
// resource.setCode("user_manager:all");
|
||||||
|
// resource.setMethod("GET");
|
||||||
|
// resource.setUrl("/users/a");
|
||||||
|
//
|
||||||
|
// ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
// objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||||
|
//
|
||||||
|
// System.out.println(objectMapper.writeValueAsString(resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMatcher() {
|
||||||
|
MvcRequestMatcher mvcRequestMatcher = new MvcRequestMatcher(new HandlerMappingIntrospector(), "/users/{id}");
|
||||||
|
System.out.println(mvcRequestMatcher.matches(new MockHttpServletRequest("GET", "/users/1")));
|
||||||
|
System.out.println(mvcRequestMatcher.matches(new MockHttpServletRequest("GET", "/users/aaa")));
|
||||||
|
System.out.println(mvcRequestMatcher.matches(new MockHttpServletRequest("GET", "/users")));
|
||||||
|
System.out.println(mvcRequestMatcher.matches(new MockHttpServletRequest("POST", "/users/1")));
|
||||||
|
System.out.println(mvcRequestMatcher.matches(new MockHttpServletRequest("PUT", "/users/1")));
|
||||||
|
System.out.println(mvcRequestMatcher.matches(new MockHttpServletRequest("DELETE", "/users/1")));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication.service.impl;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import com.springboot.cloud.auth.authentication.entity.Popedom;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.security.access.SecurityConfig;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class AuthenticationServiceImplTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsMatch_假如存在如上资源信息_当给定包含在资源信息时_那么返回true() {
|
||||||
|
AuthenticationServiceImpl authenticationServiceImpl = new AuthenticationServiceImpl();
|
||||||
|
Popedom popedom = new Popedom();
|
||||||
|
popedom.setApplicationCode("user_manager:view");
|
||||||
|
Set<Popedom> popedoms = Sets.newHashSet(popedom);
|
||||||
|
// Assert.assertTrue(authenticationServiceImpl.isMatch(new SecurityConfig("user_manager:view"), popedoms));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsMatch_假如存在如上资源信息_当给不包含在资源信息时_那么返回false() {
|
||||||
|
AuthenticationServiceImpl authenticationServiceImpl = new AuthenticationServiceImpl();
|
||||||
|
Popedom popedom = new Popedom();
|
||||||
|
popedom.setApplicationCode("user_manager:manager");
|
||||||
|
Set<Popedom> popedoms = Sets.newHashSet(popedom);
|
||||||
|
// Assert.assertFalse(authenticationServiceImpl.isMatch(new SecurityConfig("user_manager:view"), popedoms));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
package com.springboot.cloud.auth.authentication.service.impl;
|
||||||
|
|
||||||
|
import com.springboot.cloud.auth.authentication.provider.PopedomProvider;
|
||||||
|
import com.springboot.cloud.auth.authentication.rest.HttpServletRequestAuthWrapper;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
import org.springframework.security.access.ConfigAttribute;
|
||||||
|
|
||||||
|
//@RunWith(SpringRunner.class)
|
||||||
|
//@SpringBootTest
|
||||||
|
public class PopedomServiceImplTest {
|
||||||
|
|
||||||
|
/* private Map<RequestMatcher, ConfigAttribute> resourceConfigAttributes = new HashMap() {
|
||||||
|
{
|
||||||
|
MvcRequestMatcher mvcRequestMatcher1 = new MvcRequestMatcher(new HandlerMappingIntrospector(), "/users");
|
||||||
|
mvcRequestMatcher1.setMethod(HttpMethod.resolve("POST"));
|
||||||
|
MvcRequestMatcher mvcRequestMatcher2 = new MvcRequestMatcher(new HandlerMappingIntrospector(), "/users/{id}");
|
||||||
|
mvcRequestMatcher2.setMethod(HttpMethod.resolve("PUT"));
|
||||||
|
MvcRequestMatcher mvcRequestMatcher3 = new MvcRequestMatcher(new HandlerMappingIntrospector(), "/users/{id}");
|
||||||
|
mvcRequestMatcher3.setMethod(HttpMethod.resolve("DELETE"));
|
||||||
|
MvcRequestMatcher mvcRequestMatcher4 = new MvcRequestMatcher(new HandlerMappingIntrospector(), "/users/{id}");
|
||||||
|
mvcRequestMatcher4.setMethod(HttpMethod.resolve("GET"));
|
||||||
|
MvcRequestMatcher mvcRequestMatcher5 = new MvcRequestMatcher(new HandlerMappingIntrospector(), "/users/{id}/order");
|
||||||
|
mvcRequestMatcher5.setMethod(HttpMethod.resolve("GET"));
|
||||||
|
put(mvcRequestMatcher1, new SecurityConfig("user_manager:btn_add"));
|
||||||
|
put(mvcRequestMatcher2, new SecurityConfig("user_manager:btn_edit"));
|
||||||
|
put(mvcRequestMatcher3, new SecurityConfig("user_manager:btn_del"));
|
||||||
|
put(mvcRequestMatcher4, new SecurityConfig("user_manager:view"));
|
||||||
|
put(mvcRequestMatcher5, new SecurityConfig("user_order:view"));
|
||||||
|
}
|
||||||
|
};*/
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private PopedomServiceImpl popedomServiceImpl;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private PopedomProvider popedomProvider;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void testGetConfigAttributesByUrl_假如存在如上资源信息_当请求不存在method的资源时_那么返回NONEXISTENT_URL() {
|
||||||
|
// ConfigAttribute attributesByUrl = popedomServiceImpl
|
||||||
|
// .findConfigAttributesByUrl("", new HttpServletRequestAuthWrapper(new MockHttpServletRequest(), "/users/1/order"));
|
||||||
|
// Assert.assertEquals("NONEXISTENT_URL", attributesByUrl.getAttribute());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void testGetConfigAttributesByUrl_假如存在如上资源信息_当请求url存在参数时_那么返回匹配的资源信息() {
|
||||||
|
// ConfigAttribute attributesByUrl = popedomServiceImpl
|
||||||
|
// .findConfigAttributesByUrl("", new HttpServletRequestAuthWrapper(new MockHttpServletRequest(), "/users/1/order"));
|
||||||
|
// Assert.assertEquals("NONEXISTENT_URL", attributesByUrl.getAttribute());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void testGetConfigAttributesByUrl_假如存在如上资源信息_当请求存在的资源时_那么返回url和method都匹配的资源信息() {
|
||||||
|
// ConfigAttribute attributesByUrl = popedomServiceImpl
|
||||||
|
// .findConfigAttributesByUrl("", new HttpServletRequestAuthWrapper(new MockHttpServletRequest(), "/users"));
|
||||||
|
// Assert.assertEquals("user_manager:btn_add", attributesByUrl.getAttribute());
|
||||||
|
}
|
||||||
|
}
|
||||||
16
auth/authorization-server/.gitignore
vendored
Normal file
16
auth/authorization-server/.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
target/
|
||||||
|
logs/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
114
auth/authorization-server/pom.xml
Normal file
114
auth/authorization-server/pom.xml
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>business.chaoran</groupId>
|
||||||
|
<artifactId>authorization-server</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<packaging>war</packaging>
|
||||||
|
<!-- <packaging>jar</packaging>-->
|
||||||
|
|
||||||
|
<name>authorization-server</name>
|
||||||
|
<description>Demo Oauth2 project for Spring Cloud Oauth2 Authorization Server</description>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>business.chaoran</groupId>
|
||||||
|
<artifactId>auth</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>business.chaoran</groupId>
|
||||||
|
<artifactId>web</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
<!--oauth2认证-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-security</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>2.10.0.pr1</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-core</artifactId>
|
||||||
|
<version>2.9.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>2.9.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-annotations</artifactId>
|
||||||
|
<version>2.9.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 独立运行依赖-->
|
||||||
|
<!-- <dependency>-->
|
||||||
|
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||||
|
<!-- <artifactId>spring-boot-starter-web</artifactId>-->
|
||||||
|
<!-- </dependency>-->
|
||||||
|
|
||||||
|
|
||||||
|
<!-- tomcat容器部署运行依赖-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-simple</artifactId>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-logging</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<version>2.1.4.RELEASE</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>build-info</goal>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-war-plugin</artifactId>
|
||||||
|
<version>3.0.0</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
<finalName>authorization-server</finalName>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package com.springboot.auth.authorization;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
|
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||||
|
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||||
|
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableDiscoveryClient
|
||||||
|
@EnableFeignClients
|
||||||
|
public class Oauth2AuthorizationApplication extends SpringBootServletInitializer {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(Oauth2AuthorizationApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
|
||||||
|
return builder.sources(Oauth2AuthorizationApplication.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,179 @@
|
|||||||
|
package com.springboot.auth.authorization.config;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.springboot.auth.authorization.exception.CustomWebResponseExceptionTranslator;
|
||||||
|
import com.springboot.auth.authorization.oauth2.enhancer.CustomTokenEnhancer;
|
||||||
|
import com.springboot.auth.authorization.oauth2.granter.MobileTokenGranter;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
|
||||||
|
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.CompositeTokenGranter;
|
||||||
|
import org.springframework.security.oauth2.provider.TokenGranter;
|
||||||
|
import org.springframework.security.oauth2.provider.approval.Approval;
|
||||||
|
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
|
||||||
|
import org.springframework.security.oauth2.provider.approval.InMemoryApprovalStore;
|
||||||
|
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
|
||||||
|
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
|
||||||
|
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
|
||||||
|
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
|
||||||
|
import org.springframework.security.oauth2.provider.token.TokenStore;
|
||||||
|
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
|
||||||
|
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableAuthorizationServer
|
||||||
|
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("authenticationManagerBean")
|
||||||
|
private AuthenticationManager authenticationManager;
|
||||||
|
|
||||||
|
// @Qualifier("dataSource")
|
||||||
|
// @Autowired
|
||||||
|
// DataSource dataSource;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("userDetailsService")
|
||||||
|
UserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* jwt 对称加密密钥
|
||||||
|
*/
|
||||||
|
@Value("${spring.security.oauth2.jwt.signingKey}")
|
||||||
|
private String signingKey;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
|
||||||
|
// 支持将client参数放在header或body中
|
||||||
|
oauthServer.allowFormAuthenticationForClients();
|
||||||
|
|
||||||
|
//认证策略,默认为denyAll()
|
||||||
|
oauthServer.tokenKeyAccess("isAuthenticated()")
|
||||||
|
.checkTokenAccess("permitAll()");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
|
||||||
|
// 配置客户端信息,从数据库中读取,对应oauth_client_details表
|
||||||
|
// clients.jdbc(dataSource);
|
||||||
|
clients.inMemory().withClient("test_client").secret("$2a$10$2szDKjvKHJCWE6YQNznogOeQF3USZHmCYj1fG7YbfK.vnTgNKLzri").scopes("read")
|
||||||
|
.authorizedGrantTypes("client_credentials", "authorization_code", "mobile", "password", "refresh_token")
|
||||||
|
.redirectUris("http://www.baidu.com").accessTokenValiditySeconds(3600).refreshTokenValiditySeconds(3600);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
|
||||||
|
// 配置token的数据源、自定义的tokenServices等信息,配置身份认证器,配置认证方式,TokenStore,TokenGranter,OAuth2RequestFactory
|
||||||
|
endpoints.tokenStore(tokenStore())
|
||||||
|
.authorizationCodeServices(authorizationCodeServices())
|
||||||
|
.approvalStore(approvalStore())
|
||||||
|
.exceptionTranslator(customExceptionTranslator())
|
||||||
|
.tokenEnhancer(tokenEnhancerChain())
|
||||||
|
.authenticationManager(authenticationManager)
|
||||||
|
.userDetailsService(userDetailsService)
|
||||||
|
//update by joe_chen add granter
|
||||||
|
.tokenGranter(tokenGranter(endpoints));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义OAuth2异常处理
|
||||||
|
*
|
||||||
|
* @return CustomWebResponseExceptionTranslator
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public WebResponseExceptionTranslator<OAuth2Exception> customExceptionTranslator() {
|
||||||
|
return new CustomWebResponseExceptionTranslator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 授权信息持久化实现
|
||||||
|
* 密码模式无用
|
||||||
|
* @return JdbcApprovalStore
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public ApprovalStore approvalStore() {
|
||||||
|
InMemoryApprovalStore memoryApprovalStore = new InMemoryApprovalStore();
|
||||||
|
Approval approval = new Approval("1", "test_client", "read", 3600, Approval.ApprovalStatus.APPROVED);
|
||||||
|
memoryApprovalStore.addApprovals(Arrays.asList(approval));
|
||||||
|
// return new JdbcApprovalStore(dataSource);
|
||||||
|
return memoryApprovalStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 授权码模式持久化授权码code
|
||||||
|
* 密码模式无用
|
||||||
|
* @return JdbcAuthorizationCodeServices
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
protected AuthorizationCodeServices authorizationCodeServices() {
|
||||||
|
// 授权码存储等处理方式类,使用jdbc,操作oauth_code表
|
||||||
|
InMemoryAuthorizationCodeServices authorizationCodeServices = new InMemoryAuthorizationCodeServices();
|
||||||
|
// return new JdbcAuthorizationCodeServices(dataSource);
|
||||||
|
return authorizationCodeServices;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* token的持久化
|
||||||
|
*
|
||||||
|
* @return JwtTokenStore
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public TokenStore tokenStore() {
|
||||||
|
return new JwtTokenStore(accessTokenConverter());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义token
|
||||||
|
*
|
||||||
|
* @return tokenEnhancerChain
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public TokenEnhancerChain tokenEnhancerChain() {
|
||||||
|
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
|
||||||
|
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(new CustomTokenEnhancer(), accessTokenConverter()));
|
||||||
|
return tokenEnhancerChain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* jwt token的生成配置
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public JwtAccessTokenConverter accessTokenConverter() {
|
||||||
|
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
|
||||||
|
converter.setSigningKey(signingKey);
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置自定义的granter,手机号验证码登陆
|
||||||
|
*
|
||||||
|
* @param endpoints
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public TokenGranter tokenGranter(final AuthorizationServerEndpointsConfigurer endpoints) {
|
||||||
|
List<TokenGranter> granters = Lists.newArrayList(endpoints.getTokenGranter());
|
||||||
|
granters.add(new MobileTokenGranter(
|
||||||
|
authenticationManager,
|
||||||
|
endpoints.getTokenServices(),
|
||||||
|
endpoints.getClientDetailsService(),
|
||||||
|
endpoints.getOAuth2RequestFactory()));
|
||||||
|
return new CompositeTokenGranter(granters);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package com.springboot.auth.authorization.config;
|
||||||
|
|
||||||
|
/*
|
||||||
|
**********************************************
|
||||||
|
* DATE PERSON REASON
|
||||||
|
* 2020/12/7 FXY Created
|
||||||
|
**********************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
import com.springboot.cloud.common.web.interceptor.FeignBasicAuthRequestInterceptor;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class InterceptConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public FeignBasicAuthRequestInterceptor interceptor(){
|
||||||
|
return new FeignBasicAuthRequestInterceptor();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
package com.springboot.auth.authorization.config;
|
||||||
|
|
||||||
|
/*
|
||||||
|
**********************************************
|
||||||
|
* DATE PERSON REASON
|
||||||
|
* 2020-12-24 FXY Created
|
||||||
|
**********************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
import com.alibaba.cloud.nacos.registry.NacosAutoServiceRegistration;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.ApplicationArguments;
|
||||||
|
import org.springframework.boot.ApplicationRunner;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import javax.management.MBeanServer;
|
||||||
|
import javax.management.ObjectName;
|
||||||
|
import javax.management.Query;
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Slf4j
|
||||||
|
public class NacosRegisterConfig implements ApplicationRunner {
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private NacosAutoServiceRegistration registration;
|
||||||
|
|
||||||
|
private Integer port;
|
||||||
|
|
||||||
|
public NacosRegisterConfig() {
|
||||||
|
try {
|
||||||
|
this.port = Integer.parseInt(getTomcatPort());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取tomcat端口出错了,原因:{}", e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(ApplicationArguments args) {
|
||||||
|
if (registration != null && port != null) {
|
||||||
|
registration.setPort(port);
|
||||||
|
registration.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取tomcat端口
|
||||||
|
private String getTomcatPort() throws Exception {
|
||||||
|
MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
|
||||||
|
Set<ObjectName> objectNames = beanServer.queryNames(new ObjectName("*:type=Connector,*"), Query.match(Query.attr("protocol"), Query.value("HTTP/1.1")));
|
||||||
|
String port = objectNames.iterator().next().getKeyProperty("port");
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,83 @@
|
|||||||
|
package com.springboot.auth.authorization.config;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
|
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.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
||||||
|
import com.springboot.auth.authorization.oauth2.granter.MobileAuthenticationProvider;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
public class WebServerSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("userDetailsService")
|
||||||
|
private UserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("mobileUserDetailsService")
|
||||||
|
private UserDetailsService mobileUserDetailsService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
http.csrf().disable();
|
||||||
|
http.authorizeRequests()
|
||||||
|
.antMatchers("/actuator/**").permitAll()
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
.and()
|
||||||
|
.formLogin().permitAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注入自定义的userDetailsService实现,获取用户信息,设置密码加密方式
|
||||||
|
*
|
||||||
|
* @param authenticationManagerBuilder
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
|
||||||
|
authenticationManagerBuilder
|
||||||
|
.userDetailsService(userDetailsService)
|
||||||
|
.passwordEncoder(passwordEncoder());
|
||||||
|
// 设置手机验证码登陆的AuthenticationProvider
|
||||||
|
authenticationManagerBuilder.authenticationProvider(mobileAuthenticationProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 AuthenticationManager 注册为 bean , 方便配置 oauth server 的时候使用
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@Override
|
||||||
|
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||||
|
return super.authenticationManagerBean();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder passwordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建手机验证码登陆的AuthenticationProvider
|
||||||
|
*
|
||||||
|
* @return mobileAuthenticationProvider
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public MobileAuthenticationProvider mobileAuthenticationProvider() {
|
||||||
|
MobileAuthenticationProvider mobileAuthenticationProvider = new MobileAuthenticationProvider(this.mobileUserDetailsService);
|
||||||
|
mobileAuthenticationProvider.setPasswordEncoder(passwordEncoder());
|
||||||
|
return mobileAuthenticationProvider;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package com.springboot.auth.authorization.entity;
|
||||||
|
|
||||||
|
/*
|
||||||
|
**********************************************
|
||||||
|
* DATE PERSON REASON
|
||||||
|
* 2020-12-29 FXY Created
|
||||||
|
**********************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Company {
|
||||||
|
private String companyId;
|
||||||
|
private String userId;
|
||||||
|
private String organizationId;
|
||||||
|
private String empNo;
|
||||||
|
private String position;
|
||||||
|
private Integer isDefault;
|
||||||
|
private Integer state;
|
||||||
|
private String mobile;
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
package com.springboot.auth.authorization.entity;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class User {
|
||||||
|
private String id;
|
||||||
|
private String name;
|
||||||
|
private Date registryTime;
|
||||||
|
private String identity;
|
||||||
|
private String nickName;
|
||||||
|
private String photo;
|
||||||
|
private String lastModifier;
|
||||||
|
private Date lastModifyTime;
|
||||||
|
private Double state;
|
||||||
|
private String mobile;
|
||||||
|
private String email;
|
||||||
|
private String introduce;
|
||||||
|
private String avatar;
|
||||||
|
private String signature;
|
||||||
|
private String password;
|
||||||
|
private String username;
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
package com.springboot.auth.authorization.exception;
|
||||||
|
|
||||||
|
import com.springboot.cloud.common.core.exception.ErrorType;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum AuthErrorType implements ErrorType {
|
||||||
|
|
||||||
|
INVALID_REQUEST("040001", "无效请求"),
|
||||||
|
INVALID_CLIENT("040002", "无效client_id"),
|
||||||
|
INVALID_GRANT("040003", "无效授权"),
|
||||||
|
INVALID_SCOPE("040004", "无效scope"),
|
||||||
|
INVALID_TOKEN("040005", "无效token"),
|
||||||
|
INSUFFICIENT_SCOPE("040010", "授权不足"),
|
||||||
|
REDIRECT_URI_MISMATCH("040020", "redirect url不匹配"),
|
||||||
|
ACCESS_DENIED("040030", "拒绝访问"),
|
||||||
|
METHOD_NOT_ALLOWED("040040", "不支持该方法"),
|
||||||
|
SERVER_ERROR("040050", "权限服务错误"),
|
||||||
|
UNAUTHORIZED_CLIENT("040060", "未授权客户端"),
|
||||||
|
UNAUTHORIZED("040061", "未授权"),
|
||||||
|
UNSUPPORTED_RESPONSE_TYPE("040070", " 支持的响应类型"),
|
||||||
|
UNSUPPORTED_GRANT_TYPE("040071", "不支持的授权类型");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误类型码
|
||||||
|
*/
|
||||||
|
private String code;
|
||||||
|
/**
|
||||||
|
* 错误类型描述信息
|
||||||
|
*/
|
||||||
|
private String mesg;
|
||||||
|
|
||||||
|
AuthErrorType(String code, String mesg) {
|
||||||
|
this.code = code;
|
||||||
|
this.mesg = mesg;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package com.springboot.auth.authorization.exception;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.springboot.cloud.common.core.entity.vo.Result;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
|
||||||
|
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Data
|
||||||
|
@JsonSerialize(using = CustomOauthExceptionSerializer.class)
|
||||||
|
class CustomOauthException extends OAuth2Exception {
|
||||||
|
|
||||||
|
private final Result result;
|
||||||
|
|
||||||
|
CustomOauthException(OAuth2Exception oAuth2Exception) {
|
||||||
|
super(oAuth2Exception.getSummary(), oAuth2Exception);
|
||||||
|
this.result = Result.fail(AuthErrorType.valueOf(oAuth2Exception.getOAuth2ErrorCode().toUpperCase()), oAuth2Exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package com.springboot.auth.authorization.exception;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class CustomOauthExceptionSerializer extends StdSerializer<CustomOauthException> {
|
||||||
|
public CustomOauthExceptionSerializer() {
|
||||||
|
super(CustomOauthException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(CustomOauthException value, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
||||||
|
gen.writeObject(value.getResult());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package com.springboot.auth.authorization.exception;
|
||||||
|
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
|
||||||
|
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
|
||||||
|
|
||||||
|
public class CustomWebResponseExceptionTranslator implements WebResponseExceptionTranslator<OAuth2Exception> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseEntity<OAuth2Exception> translate(Exception e) {
|
||||||
|
|
||||||
|
OAuth2Exception oAuth2Exception = (OAuth2Exception) e;
|
||||||
|
return ResponseEntity.status(oAuth2Exception.getHttpErrorCode())
|
||||||
|
.body(new CustomOauthException(oAuth2Exception));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.springboot.auth.authorization.exception;
|
||||||
|
|
||||||
|
import com.springboot.cloud.common.web.exception.DefaultGlobalExceptionHandlerAdvice;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class GlobalExceptionHandlerAdvice extends DefaultGlobalExceptionHandlerAdvice {
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
package com.springboot.auth.authorization.oauth2;
|
||||||
|
|
||||||
|
import com.springboot.auth.authorization.entity.Company;
|
||||||
|
import com.springboot.auth.authorization.entity.User;
|
||||||
|
import com.springboot.auth.authorization.oauth2.authority.CustomGrantedAuthority;
|
||||||
|
import com.springboot.auth.authorization.service.UserService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service("userDetailsService")
|
||||||
|
public class CustomUserDetailsService implements UserDetailsService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String payload) {
|
||||||
|
|
||||||
|
User user = userService.getUserByUsernameOrMobile(payload);
|
||||||
|
log.info("load user by username :{}", user.toString());
|
||||||
|
|
||||||
|
return new org.springframework.security.core.userdetails.User(
|
||||||
|
user.getUsername(),
|
||||||
|
user.getPassword(), //加了密的密码
|
||||||
|
user.getState() == 1, //账号是否可用
|
||||||
|
user.getState() == 1, //账号未过期
|
||||||
|
true, //密码未过期
|
||||||
|
user.getState() == 1, //账户未锁定
|
||||||
|
this.obtainGrantedCompany(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得登录者所有的公司信息
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected Set<CustomGrantedAuthority> obtainGrantedCompany(User user) {
|
||||||
|
List<Company> companies = userService.getUserAllCompany(user.getId());
|
||||||
|
return companies.stream().map(companyUser -> new CustomGrantedAuthority(companyUser.getCompanyId(), companyUser.getIsDefault() == 1)).collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
package com.springboot.auth.authorization.oauth2;
|
||||||
|
|
||||||
|
import com.springboot.auth.authorization.entity.User;
|
||||||
|
import com.springboot.auth.authorization.provider.SmsCodeProvider;
|
||||||
|
import com.springboot.auth.authorization.service.UserService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手机验证码登陆, 用户相关获取
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service("mobileUserDetailsService")
|
||||||
|
public class MobileUserDetailsService extends CustomUserDetailsService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
@Autowired
|
||||||
|
private SmsCodeProvider smsCodeProvider;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String payload) {
|
||||||
|
User user = userService.getUserByUsernameOrMobile(payload);
|
||||||
|
log.info("load user by mobile:{}", user.toString());
|
||||||
|
|
||||||
|
// 如果为mobile模式,从短信服务中获取验证码(动态密码)
|
||||||
|
String credentials = smsCodeProvider.getSmsCode(payload, "LOGIN");
|
||||||
|
|
||||||
|
return new org.springframework.security.core.userdetails.User(
|
||||||
|
user.getUsername(),
|
||||||
|
credentials,
|
||||||
|
user.getState() == 1 ? true : false,
|
||||||
|
user.getState() == 1 ? true : false,
|
||||||
|
user.getState() == 1 ? true : false,
|
||||||
|
user.getState() == 1 ? true : false,
|
||||||
|
super.obtainGrantedCompany(user));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package com.springboot.auth.authorization.oauth2.authority;
|
||||||
|
|
||||||
|
/*
|
||||||
|
**********************************************
|
||||||
|
* DATE PERSON REASON
|
||||||
|
* 2021-01-05 FXY Created
|
||||||
|
**********************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据getter方法进行序列化的
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CustomGrantedAuthority implements GrantedAuthority {
|
||||||
|
|
||||||
|
private final String company;
|
||||||
|
private boolean isDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 后期可换为角色编号
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getAuthority() {
|
||||||
|
return company;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDefault() {
|
||||||
|
return isDefault;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package com.springboot.auth.authorization.oauth2.enhancer;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.springboot.auth.authorization.oauth2.authority.CustomGrantedAuthority;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
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 java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class CustomTokenEnhancer implements TokenEnhancer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
|
||||||
|
Map<String, Object> additionalInfo = Maps.newHashMap();
|
||||||
|
additionalInfo.put("companies", authentication.getAuthorities());
|
||||||
|
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package com.springboot.auth.authorization.oauth2.granter;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
|
||||||
|
public class MobileAuthenticationProvider extends DaoAuthenticationProvider {
|
||||||
|
|
||||||
|
public MobileAuthenticationProvider(UserDetailsService userDetailsService) {
|
||||||
|
super.setUserDetailsService(userDetailsService);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(Class<?> authentication) {
|
||||||
|
return MobileAuthenticationToken.class.isAssignableFrom(authentication);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.springboot.auth.authorization.oauth2.granter;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
|
||||||
|
public class MobileAuthenticationToken extends UsernamePasswordAuthenticationToken {
|
||||||
|
|
||||||
|
public MobileAuthenticationToken(Authentication authenticationToken) {
|
||||||
|
super(authenticationToken.getPrincipal(), authenticationToken.getCredentials());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
package com.springboot.auth.authorization.oauth2.granter;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.*;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
|
||||||
|
import org.springframework.security.oauth2.provider.*;
|
||||||
|
import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
|
||||||
|
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class MobileTokenGranter extends ResourceOwnerPasswordTokenGranter {
|
||||||
|
|
||||||
|
private static final String GRANT_TYPE = "mobile";
|
||||||
|
|
||||||
|
private AuthenticationManager authenticationManager;
|
||||||
|
|
||||||
|
public MobileTokenGranter(AuthenticationManager authenticationManager,
|
||||||
|
AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService,
|
||||||
|
OAuth2RequestFactory requestFactory) {
|
||||||
|
super(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
|
||||||
|
this.authenticationManager = authenticationManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
|
||||||
|
Map<String, String> parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters());
|
||||||
|
String username = parameters.get("no");
|
||||||
|
String password = parameters.get("password");
|
||||||
|
// Protect from downstream leaks of password
|
||||||
|
parameters.remove("no");
|
||||||
|
|
||||||
|
Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
|
||||||
|
MobileAuthenticationToken mobileAuthenticationToken = new MobileAuthenticationToken(userAuth);
|
||||||
|
((AbstractAuthenticationToken) userAuth).setDetails(parameters);
|
||||||
|
try {
|
||||||
|
userAuth = this.authenticationManager.authenticate(mobileAuthenticationToken);
|
||||||
|
} catch (AccountStatusException ase) {
|
||||||
|
//covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
|
||||||
|
throw new InvalidGrantException(ase.getMessage());
|
||||||
|
} catch (BadCredentialsException e) {
|
||||||
|
// If the username/password are wrong the spec says we should send 400/invalid grant
|
||||||
|
throw new InvalidGrantException(e.getMessage());
|
||||||
|
}
|
||||||
|
if (userAuth == null || !userAuth.isAuthenticated()) {
|
||||||
|
throw new InvalidGrantException("Could not authenticate user: " + username);
|
||||||
|
}
|
||||||
|
|
||||||
|
OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
|
||||||
|
return new OAuth2Authentication(storedOAuth2Request, mobileAuthenticationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package com.springboot.auth.authorization.provider;
|
||||||
|
|
||||||
|
import com.springboot.auth.authorization.entity.Company;
|
||||||
|
import com.springboot.auth.authorization.entity.User;
|
||||||
|
import com.springboot.cloud.common.core.entity.vo.Result;
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@FeignClient(name = "organization", fallback = OrganizationProviderFallback.class,path = "organization")
|
||||||
|
//@FeignClient(name = "organization", fallback = OrganizationProviderFallback.class)
|
||||||
|
public interface OrganizationProvider {
|
||||||
|
|
||||||
|
@GetMapping(value = "/user/getUserByUsernameOrMobile")
|
||||||
|
Result<User> getUserByUsernameOrMobile(@RequestParam("payload")String payload);
|
||||||
|
|
||||||
|
@GetMapping(value = "/company/getUserAllCompany")
|
||||||
|
Result<List<Company>> getUserAllCompany(@RequestParam("userId")String userId);
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package com.springboot.auth.authorization.provider;
|
||||||
|
|
||||||
|
import com.springboot.auth.authorization.entity.Company;
|
||||||
|
import com.springboot.auth.authorization.entity.User;
|
||||||
|
import com.springboot.cloud.common.core.entity.vo.Result;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class OrganizationProviderFallback implements OrganizationProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<User> getUserByUsernameOrMobile(String payload) {
|
||||||
|
return Result.success(new User());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<List<Company>> getUserAllCompany(String userId) {
|
||||||
|
return Result.success(new HashSet<Company>());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package com.springboot.auth.authorization.provider;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* todo 实现短信验证码的服务
|
||||||
|
*/
|
||||||
|
//@FeignClient(name = "sms", fallback = OrganizationProviderFallback.class,path="sms")
|
||||||
|
public interface SmsCodeProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mobile
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GetMapping(value = "/sms/{mobile}")
|
||||||
|
String getSmsCode(@PathVariable("mobile") String mobile, @RequestParam("businessType") String businessType);
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package com.springboot.auth.authorization.provider;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SmsCodeProviderFallback implements SmsCodeProvider {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSmsCode(String mobile, String businessType) {
|
||||||
|
// 该类为mock, 目前暂时没有sms的服务
|
||||||
|
return passwordEncoder.encode("123456");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package com.springboot.auth.authorization.service;
|
||||||
|
|
||||||
|
import com.springboot.auth.authorization.entity.Company;
|
||||||
|
import com.springboot.auth.authorization.entity.User;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface UserService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过账号或者手机号得到用户信息
|
||||||
|
* @param payload 账号或者手机号
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
User getUserByUsernameOrMobile(String payload);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过用户编号得到所有所属公司
|
||||||
|
*/
|
||||||
|
List<Company> getUserAllCompany(String userId);
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package com.springboot.auth.authorization.service.impl;
|
||||||
|
|
||||||
|
import com.springboot.auth.authorization.entity.Company;
|
||||||
|
import com.springboot.auth.authorization.entity.User;
|
||||||
|
import com.springboot.auth.authorization.provider.OrganizationProvider;
|
||||||
|
import com.springboot.auth.authorization.service.UserService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class UserServiceImpl implements UserService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OrganizationProvider organizationProvider;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User getUserByUsernameOrMobile(String payload) {
|
||||||
|
return organizationProvider.getUserByUsernameOrMobile(payload).getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Company> getUserAllCompany(String userId) {
|
||||||
|
return organizationProvider.getUserAllCompany(userId).getData();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package com.springboot.auth.authorization;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
||||||
|
|
||||||
|
public class ApplicationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void contextLoads() {
|
||||||
|
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||||
|
System.out.println(passwordEncoder.encode("test_secret"));
|
||||||
|
}
|
||||||
|
}
|
||||||
68
auth/pom.xml
Normal file
68
auth/pom.xml
Normal file
@ -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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>business.chaoran</groupId>
|
||||||
|
<artifactId>auth</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<name>auth</name>
|
||||||
|
<description>Demo Auth project for Spring Boot</description>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>business.chaoran</groupId>
|
||||||
|
<artifactId>xin-cloud</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>authentication-server</module>
|
||||||
|
<module>authentication-client</module>
|
||||||
|
<module>authorization-server</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security.oauth</groupId>
|
||||||
|
<artifactId>spring-security-oauth2</artifactId>
|
||||||
|
<version>[2.2.4,)</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-jwt</artifactId>
|
||||||
|
<version>RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!--mybatis依赖包-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mybatis.spring.boot</groupId>
|
||||||
|
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||||
|
<version>1.3.2</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>druid-spring-boot-starter</artifactId>
|
||||||
|
<version>1.1.9</version>
|
||||||
|
</dependency>
|
||||||
|
<!--数据库-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
|
<version>8.0.18</version>
|
||||||
|
</dependency>
|
||||||
|
<!--Redis缓存-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
25
common/core/.gitignore
vendored
Normal file
25
common/core/.gitignore
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/build/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
40
common/core/pom.xml
Normal file
40
common/core/pom.xml
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>business.chaoran</groupId>
|
||||||
|
<artifactId>core</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>core</name>
|
||||||
|
<description>Demo Core project for Spring Boot</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>business.chaoran</groupId>
|
||||||
|
<artifactId>common</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!--Swagger2 - RESTful API文档-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.springfox</groupId>
|
||||||
|
<artifactId>springfox-swagger2</artifactId>
|
||||||
|
<version>2.9.2</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.springfox</groupId>
|
||||||
|
<artifactId>springfox-swagger-ui</artifactId>
|
||||||
|
<version>2.9.2</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
@ -0,0 +1,167 @@
|
|||||||
|
package com.springboot.cloud.common.core.entity.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.springboot.cloud.common.core.exception.BaseException;
|
||||||
|
import com.springboot.cloud.common.core.exception.ErrorType;
|
||||||
|
import com.springboot.cloud.common.core.exception.SystemErrorType;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
|
||||||
|
@ApiModel(description = "rest请求的返回模型,所有rest正常都返回该类的对象")
|
||||||
|
@Getter
|
||||||
|
public class Result<T> {
|
||||||
|
|
||||||
|
public static final String SUCCESSFUL_CODE = "000000";
|
||||||
|
public static final String SUCCESSFUL_MESG = "处理成功";
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "处理结果code", required = true)
|
||||||
|
private String code;
|
||||||
|
@ApiModelProperty(value = "处理结果描述信息")
|
||||||
|
private String msg;
|
||||||
|
@ApiModelProperty(value = "请求结果生成时间戳")
|
||||||
|
private Instant time;
|
||||||
|
@ApiModelProperty(value = "处理结果数据信息")
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
private T data;
|
||||||
|
|
||||||
|
public Result() {
|
||||||
|
this.time = ZonedDateTime.now().toInstant();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param errorType
|
||||||
|
*/
|
||||||
|
public Result(ErrorType errorType) {
|
||||||
|
this.code = errorType.getCode();
|
||||||
|
this.msg = errorType.getMesg();
|
||||||
|
this.time = ZonedDateTime.now().toInstant();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param errorType
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
public Result(ErrorType errorType, T data) {
|
||||||
|
this(errorType);
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内部使用,用于构造成功的结果
|
||||||
|
*
|
||||||
|
* @param code
|
||||||
|
* @param msg
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
private Result(String code, String msg, T data) {
|
||||||
|
this.code = code;
|
||||||
|
this.msg = msg;
|
||||||
|
this.data = data;
|
||||||
|
this.time = ZonedDateTime.now().toInstant();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 快速创建成功结果并返回结果数据
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @return Result
|
||||||
|
*/
|
||||||
|
public static Result success(Object data) {
|
||||||
|
return new Result<>(SUCCESSFUL_CODE, SUCCESSFUL_MESG, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 快速创建成功结果
|
||||||
|
*
|
||||||
|
* @return Result
|
||||||
|
*/
|
||||||
|
public static Result success() {
|
||||||
|
return success(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统异常类没有返回数据
|
||||||
|
*
|
||||||
|
* @return Result
|
||||||
|
*/
|
||||||
|
public static Result fail() {
|
||||||
|
return new Result(SystemErrorType.SYSTEM_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统异常类没有返回数据
|
||||||
|
*
|
||||||
|
* @param baseException
|
||||||
|
* @return Result
|
||||||
|
*/
|
||||||
|
public static Result fail(BaseException baseException) {
|
||||||
|
return fail(baseException, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统异常类并返回结果数据
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @return Result
|
||||||
|
*/
|
||||||
|
public static Result fail(BaseException baseException, Object data) {
|
||||||
|
return new Result<>(baseException.getErrorType(), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统异常类并返回结果数据
|
||||||
|
*
|
||||||
|
* @param errorType
|
||||||
|
* @param data
|
||||||
|
* @return Result
|
||||||
|
*/
|
||||||
|
public static Result fail(ErrorType errorType, Object data) {
|
||||||
|
return new Result<>(errorType, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统异常类并返回结果数据
|
||||||
|
*
|
||||||
|
* @param errorType
|
||||||
|
* @return Result
|
||||||
|
*/
|
||||||
|
public static Result fail(ErrorType errorType) {
|
||||||
|
return Result.fail(errorType, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统异常类并返回结果数据
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @return Result
|
||||||
|
*/
|
||||||
|
public static Result fail(Object data) {
|
||||||
|
return new Result<>(SystemErrorType.SYSTEM_ERROR, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功code=000000
|
||||||
|
*
|
||||||
|
* @return true/false
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return SUCCESSFUL_CODE.equals(this.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败
|
||||||
|
*
|
||||||
|
* @return true/false
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public boolean isFail() {
|
||||||
|
return !isSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package com.springboot.cloud.common.core.exception;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class BaseException extends RuntimeException {
|
||||||
|
/**
|
||||||
|
* 异常对应的错误类型
|
||||||
|
*/
|
||||||
|
private final ErrorType errorType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认是系统异常
|
||||||
|
*/
|
||||||
|
public BaseException() {
|
||||||
|
this.errorType = SystemErrorType.SYSTEM_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseException(ErrorType errorType) {
|
||||||
|
this.errorType = errorType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseException(ErrorType errorType, String message) {
|
||||||
|
super(message);
|
||||||
|
this.errorType = errorType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseException(ErrorType errorType, String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
this.errorType = errorType;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
package com.springboot.cloud.common.core.exception;
|
||||||
|
|
||||||
|
public interface ErrorType {
|
||||||
|
/**
|
||||||
|
* 返回code
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
String getCode();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回mesg
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
String getMesg();
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.springboot.cloud.common.core.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by zhoutaoo on 2018/6/2.
|
||||||
|
*/
|
||||||
|
public class ServiceException extends BaseException {
|
||||||
|
|
||||||
|
//TODO 对业务异常的返回码进行校验,规范到一定范围内
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package com.springboot.cloud.common.core.exception;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum SystemErrorType implements ErrorType {
|
||||||
|
|
||||||
|
SYSTEM_ERROR("-1", "系统异常"),
|
||||||
|
SYSTEM_BUSY("000001", "系统繁忙,请稍候再试"),
|
||||||
|
|
||||||
|
GATEWAY_NOT_FOUND_SERVICE("010404", "服务未找到"),
|
||||||
|
GATEWAY_ERROR("010500", "网关异常"),
|
||||||
|
GATEWAY_CONNECT_TIME_OUT("010002", "网关超时"),
|
||||||
|
|
||||||
|
ARGUMENT_NOT_VALID("020000", "请求参数校验不通过"),
|
||||||
|
INVALID_TOKEN("020001", "无效token"),
|
||||||
|
UPLOAD_FILE_SIZE_LIMIT("020010", "上传文件大小超过限制"),
|
||||||
|
|
||||||
|
DUPLICATE_PRIMARY_KEY("030000","唯一键冲突");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误类型码
|
||||||
|
*/
|
||||||
|
private String code;
|
||||||
|
/**
|
||||||
|
* 错误类型描述信息
|
||||||
|
*/
|
||||||
|
private String mesg;
|
||||||
|
|
||||||
|
SystemErrorType(String code, String mesg) {
|
||||||
|
this.code = code;
|
||||||
|
this.mesg = mesg;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
package com.springboot.cloud.common.core.util;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {"companies":[{"default":true,"authority":"1"},{"default":false,"authority":"2"}],"user_name":"admin","scope":["read"],"exp":1610100088,"authorities":["1","2"],"jti":"nt9bTYbUe8IzuEgfAz70nwaMveI","client_id":"test_client"}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户上下文
|
||||||
|
*/
|
||||||
|
public class UserContextHolder {
|
||||||
|
|
||||||
|
private ThreadLocal<Map<String, String>> threadLocal;
|
||||||
|
|
||||||
|
private UserContextHolder() {
|
||||||
|
this.threadLocal = new ThreadLocal<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建实例
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static UserContextHolder getInstance() {
|
||||||
|
return SingletonHolder.sInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 静态内部类单例模式
|
||||||
|
* 单例初使化
|
||||||
|
*/
|
||||||
|
private static class SingletonHolder {
|
||||||
|
private static final UserContextHolder sInstance = new UserContextHolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户上下文中放入信息
|
||||||
|
*
|
||||||
|
* @param map
|
||||||
|
*/
|
||||||
|
public void setContext(Map<String, String> map) {
|
||||||
|
threadLocal.set(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取上下文中的信息
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Map<String, String> getContext() {
|
||||||
|
return threadLocal.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取上下文中的用户名
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getUsername() {
|
||||||
|
return Optional.ofNullable(threadLocal.get()).orElse(Maps.newHashMap()).get("user_name");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取上下文中用户登录的公司
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getCurrentCompany() {
|
||||||
|
return Optional.ofNullable(threadLocal.get()).orElse(Maps.newHashMap()).get("company");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空上下文
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
threadLocal.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
46
common/pom.xml
Normal file
46
common/pom.xml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>business.chaoran</groupId>
|
||||||
|
<artifactId>common</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<name>common</name>
|
||||||
|
<description>Demo Common project for Spring Boot</description>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>core</module>
|
||||||
|
<module>web</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-lang</groupId>
|
||||||
|
<artifactId>commons-lang</artifactId>
|
||||||
|
<version>2.6</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-core</artifactId>
|
||||||
|
<version>5.0.11.RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
<!--使用 lombok 简化 Java 代码-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.10</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<!--测试-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.12</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
102
common/web/pom.xml
Normal file
102
common/web/pom.xml
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>business.chaoran</groupId>
|
||||||
|
<artifactId>web</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>web</name>
|
||||||
|
<description>Demo Web project for Spring Boot</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>business.chaoran</groupId>
|
||||||
|
<artifactId>common</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>business.chaoran</groupId>
|
||||||
|
<artifactId>core</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
<!--spring web相关包-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-web</artifactId>
|
||||||
|
<version>5.0.4.RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-webmvc</artifactId>
|
||||||
|
<version>5.1.0.RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>2.10.0.pr1</version>
|
||||||
|
</dependency>
|
||||||
|
<!--mybatis plus依赖包-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mybatis</groupId>
|
||||||
|
<artifactId>mybatis-spring</artifactId>
|
||||||
|
<version>2.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
<!--Redis缓存-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
<version>2.1.4.RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-pool2</artifactId>
|
||||||
|
<version>2.6.0</version>
|
||||||
|
</dependency>
|
||||||
|
<!--开发相关-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<!--测试框架-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-test</artifactId>
|
||||||
|
<version>5.1.6.RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
<version>RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mybatis</groupId>
|
||||||
|
<artifactId>mybatis</artifactId>
|
||||||
|
<version>3.5.1</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.openfeign</groupId>
|
||||||
|
<artifactId>feign-core</artifactId>
|
||||||
|
<version>10.1.0</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
package com.springboot.cloud.common.web.entity.form;
|
||||||
|
|
||||||
|
import com.springboot.cloud.common.web.entity.po.BasePo;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
|
||||||
|
@ApiModel
|
||||||
|
@Slf4j
|
||||||
|
@Data
|
||||||
|
public class BaseForm<T extends BasePo> {
|
||||||
|
/**
|
||||||
|
* 用户名
|
||||||
|
*/
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form转化为Po,进行后续业务处理
|
||||||
|
*
|
||||||
|
* @param clazz
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public T toPo(Class<T> clazz) {
|
||||||
|
T t = BeanUtils.instantiateClass(clazz);
|
||||||
|
BeanUtils.copyProperties(this, t);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form转化为Po,进行后续业务处理
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @param clazz
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public T toPo(String id, Class<T> clazz) {
|
||||||
|
T t = BeanUtils.instantiateClass(clazz);
|
||||||
|
t.setId(id);
|
||||||
|
BeanUtils.copyProperties(this, t);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
package com.springboot.cloud.common.web.entity.form;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.springboot.cloud.common.web.entity.param.BaseParam;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
|
||||||
|
@ApiModel
|
||||||
|
@Slf4j
|
||||||
|
@Data
|
||||||
|
public class BaseQueryForm<P extends BaseParam> extends BaseForm {
|
||||||
|
/**
|
||||||
|
* 分页查询的参数,当前页数
|
||||||
|
*/
|
||||||
|
private long current = 1;
|
||||||
|
/**
|
||||||
|
* 分页查询的参数,当前页面每页显示的数量
|
||||||
|
*/
|
||||||
|
private long size = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form转化为Param
|
||||||
|
*
|
||||||
|
* @param clazz
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public P toParam(Class<P> clazz) {
|
||||||
|
P p = BeanUtils.instantiateClass(clazz);
|
||||||
|
BeanUtils.copyProperties(this, p);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从form中获取page参数,用于分页查询参数
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Page getPage() {
|
||||||
|
return new Page(this.getCurrent(), this.getSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package com.springboot.cloud.common.web.entity.param;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.springboot.cloud.common.web.entity.po.BasePo;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class BaseParam<T extends BasePo> {
|
||||||
|
private Date createdTimeStart;
|
||||||
|
private Date createdTimeEnd;
|
||||||
|
|
||||||
|
public QueryWrapper<T> build() {
|
||||||
|
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.ge(null != this.createdTimeStart, "created_time", this.createdTimeStart)
|
||||||
|
.le(null != this.createdTimeEnd, "created_time", this.createdTimeEnd);
|
||||||
|
return queryWrapper;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package com.springboot.cloud.common.web.entity.po;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class BasePo implements Serializable {
|
||||||
|
public final static String DEFAULT_USERNAME = "system";
|
||||||
|
@TableId(type = IdType.ID_WORKER_STR)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private String createdBy;
|
||||||
|
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Date createdTime;
|
||||||
|
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private String updatedBy;
|
||||||
|
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Date updatedTime;
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package com.springboot.cloud.common.web.entity.vo;
|
||||||
|
|
||||||
|
import com.springboot.cloud.common.web.entity.po.BasePo;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class BaseVo<T extends BasePo> implements Serializable {
|
||||||
|
private String id;
|
||||||
|
}
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
package com.springboot.cloud.common.web.exception;
|
||||||
|
|
||||||
|
import com.springboot.cloud.common.core.entity.vo.Result;
|
||||||
|
import com.springboot.cloud.common.core.exception.BaseException;
|
||||||
|
import com.springboot.cloud.common.core.exception.SystemErrorType;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.dao.DuplicateKeyException;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
|
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
import org.springframework.web.multipart.MultipartException;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class DefaultGlobalExceptionHandlerAdvice {
|
||||||
|
|
||||||
|
@ExceptionHandler(value = {MissingServletRequestParameterException.class})
|
||||||
|
public Result missingServletRequestParameterException(MissingServletRequestParameterException ex) {
|
||||||
|
log.error("missing servlet request parameter exception:{}", ex.getMessage());
|
||||||
|
return Result.fail(SystemErrorType.ARGUMENT_NOT_VALID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(value = {MultipartException.class})
|
||||||
|
public Result uploadFileLimitException(MultipartException ex) {
|
||||||
|
log.error("upload file size limit:{}", ex.getMessage());
|
||||||
|
return Result.fail(SystemErrorType.UPLOAD_FILE_SIZE_LIMIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(value = {MethodArgumentNotValidException.class})
|
||||||
|
public Result serviceException(MethodArgumentNotValidException ex) {
|
||||||
|
log.error("service exception:{}", ex.getMessage());
|
||||||
|
return Result.fail(SystemErrorType.ARGUMENT_NOT_VALID, ex.getBindingResult().getFieldError().getDefaultMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(value = {DuplicateKeyException.class})
|
||||||
|
public Result duplicateKeyException(DuplicateKeyException ex) {
|
||||||
|
log.error("primary key duplication exception:{}", ex.getMessage());
|
||||||
|
return Result.fail(SystemErrorType.DUPLICATE_PRIMARY_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(value = {BaseException.class})
|
||||||
|
public Result baseException(BaseException ex) {
|
||||||
|
log.error("base exception:{}", ex.getMessage());
|
||||||
|
return Result.fail(ex.getErrorType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(value = {Exception.class})
|
||||||
|
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||||
|
public Result exception() {
|
||||||
|
return Result.fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(value = {Throwable.class})
|
||||||
|
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||||
|
public Result throwable() {
|
||||||
|
return Result.fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package com.springboot.cloud.common.web.handler;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||||
|
import com.springboot.cloud.common.core.util.UserContextHolder;
|
||||||
|
import com.springboot.cloud.common.web.entity.po.BasePo;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.ibatis.reflection.MetaObject;
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class PoMetaObjectHandler implements MetaObjectHandler {
|
||||||
|
/**
|
||||||
|
* 获取当前访问用户,为空返回默认system
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String getCurrentUsername() {
|
||||||
|
return StringUtils.defaultIfBlank(UserContextHolder.getInstance().getUsername(), BasePo.DEFAULT_USERNAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertFill(MetaObject metaObject) {
|
||||||
|
this.setInsertFieldValByName("createdBy", getCurrentUsername(), metaObject);
|
||||||
|
this.setInsertFieldValByName("createdTime", Date.from(ZonedDateTime.now().toInstant()), metaObject);
|
||||||
|
this.updateFill(metaObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateFill(MetaObject metaObject) {
|
||||||
|
this.setUpdateFieldValByName("updatedBy", getCurrentUsername(), metaObject);
|
||||||
|
this.setUpdateFieldValByName("updatedTime", Date.from(ZonedDateTime.now().toInstant()), metaObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
package com.springboot.cloud.common.web.interceptor;
|
||||||
|
|
||||||
|
/*
|
||||||
|
**********************************************
|
||||||
|
* DATE PERSON REASON
|
||||||
|
* 2020-12-04 FXY Created
|
||||||
|
**********************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import feign.RequestInterceptor;
|
||||||
|
import feign.RequestTemplate;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.util.DigestUtils;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class FeignBasicAuthRequestInterceptor implements RequestInterceptor {
|
||||||
|
|
||||||
|
public FeignBasicAuthRequestInterceptor(){
|
||||||
|
log.info("========================加载自定义拦截器=========================");
|
||||||
|
}
|
||||||
|
|
||||||
|
//服务间调用头
|
||||||
|
private static final String SERVER_CLIENT_TOKEN="server-client-token";
|
||||||
|
//服务间调用秘钥
|
||||||
|
private static final String SERVER_CLIENT_SECRET="server";
|
||||||
|
//网关调用服务头
|
||||||
|
private static final String X_CLIENT_TOKEN="x-client-token";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(RequestTemplate requestTemplate) {
|
||||||
|
String url = requestTemplate.path();
|
||||||
|
Map<String, Collection<String>> headers = requestTemplate.headers();
|
||||||
|
if(!headers.containsKey(X_CLIENT_TOKEN)){
|
||||||
|
String method = requestTemplate.method();
|
||||||
|
String originStr = new StringBuilder().append(url).append(method).append(SERVER_CLIENT_TOKEN).append(SERVER_CLIENT_SECRET).toString();
|
||||||
|
// 加入自定义头信息
|
||||||
|
String md5Secret = DigestUtils.md5DigestAsHex(originStr.getBytes());
|
||||||
|
requestTemplate.header(SERVER_CLIENT_TOKEN, md5Secret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
package com.springboot.cloud.common.web.interceptor;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.springboot.cloud.common.core.util.UserContextHolder;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.util.DigestUtils;
|
||||||
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户信息拦截器
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class UserInterceptor implements HandlerInterceptor {
|
||||||
|
/**
|
||||||
|
* 服务间调用token用户信息,格式为json
|
||||||
|
* {
|
||||||
|
* "user_name":"必须有"
|
||||||
|
* "自定义key:"value"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
//获取相关授权信息请求头
|
||||||
|
private static final String X_CLIENT_TOKEN_USER = "x-client-token-user";
|
||||||
|
|
||||||
|
//网关调用服务请求头
|
||||||
|
private static final String X_CLIENT_TOKEN = "x-client-token";
|
||||||
|
|
||||||
|
//网关调用服务秘钥
|
||||||
|
private static final String X_CLIENT_SECRET = "gateway-web";
|
||||||
|
|
||||||
|
//服务间调用请求头
|
||||||
|
private static final String SERVER_CLIENT_TOKEN = "server-client-token";
|
||||||
|
|
||||||
|
//服务间调用秘钥
|
||||||
|
private static final String SERVER_CLIENT_SECRET = "server";
|
||||||
|
|
||||||
|
//当前用户登录公司
|
||||||
|
private static final String USER_CURRENT_COMPANY = "company";
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||||
|
//从网关获取并校验,通过校验就可信任x-client-token-user中的信息
|
||||||
|
boolean b = checkToken(request);
|
||||||
|
if (!b) {
|
||||||
|
//重定向到登录前端
|
||||||
|
response.sendRedirect("https://www.baidu.com");
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
//放入上下文数据
|
||||||
|
String userInfoString = StringUtils.defaultIfBlank(request.getHeader(X_CLIENT_TOKEN_USER), "{}");
|
||||||
|
Map context = new ObjectMapper().readValue(userInfoString, Map.class);
|
||||||
|
context.put("company", request.getHeader(USER_CURRENT_COMPANY));
|
||||||
|
UserContextHolder.getInstance().setContext(context);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkToken(HttpServletRequest request) {
|
||||||
|
String method = request.getMethod();
|
||||||
|
String url = request.getServletPath();
|
||||||
|
Optional<String> serverToken = Optional.ofNullable(request.getHeader(SERVER_CLIENT_TOKEN));
|
||||||
|
Optional<String> clientToken = Optional.ofNullable(request.getHeader(X_CLIENT_TOKEN));
|
||||||
|
if (serverToken.isPresent()) {
|
||||||
|
//如果是服务间相互调用不需要加contextPath,因为feign拦截器中加密是根据servletPath来加密的,此处也是根据servletPath解密的
|
||||||
|
String originStr = new StringBuilder().append(url).append(method).append(SERVER_CLIENT_TOKEN).append(SERVER_CLIENT_SECRET).toString();
|
||||||
|
return DigestUtils.md5DigestAsHex(originStr.getBytes()).equals(serverToken.get());
|
||||||
|
} else if (clientToken.isPresent()) {
|
||||||
|
//如果是网关调用服务,网关服务需要加上contextPath
|
||||||
|
String originStr = new StringBuilder().append(request.getContextPath().concat(url)).append(method).append(X_CLIENT_TOKEN).append(X_CLIENT_SECRET).toString();
|
||||||
|
return DigestUtils.md5DigestAsHex(originStr.getBytes()).equals(clientToken.get());
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
|
||||||
|
UserContextHolder.getInstance().clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
package com.springboot.cloud.common.web.redis;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
|
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.springframework.cache.CacheManager;
|
||||||
|
import org.springframework.cache.annotation.CachingConfigurerSupport;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||||
|
import org.springframework.data.redis.cache.RedisCacheManager;
|
||||||
|
import org.springframework.data.redis.cache.RedisCacheWriter;
|
||||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
|
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
||||||
|
import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class RedisConfig extends CachingConfigurerSupport {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CacheManager cacheManager(RedisConnectionFactory factory) {
|
||||||
|
//对象的序列化
|
||||||
|
RedisSerializationContext.SerializationPair valueSerializationPair
|
||||||
|
= RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer());
|
||||||
|
//全局redis缓存过期时间
|
||||||
|
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
|
||||||
|
.entryTtl(Duration.ofDays(1))
|
||||||
|
// .serializeKeysWith()
|
||||||
|
.serializeValuesWith(valueSerializationPair);
|
||||||
|
|
||||||
|
return new RedisCacheManager(RedisCacheWriter.nonLockingRedisCacheWriter(factory), redisCacheConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {
|
||||||
|
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
|
||||||
|
jackson2JsonRedisSerializer.setObjectMapper(objectMapper());
|
||||||
|
return jackson2JsonRedisSerializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObjectMapper objectMapper() {
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||||
|
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
|
||||||
|
return objectMapper;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package com.springboot.cloud.common.web.exception;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class DefaultGlobalExceptionHandlerAdviceTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMethod() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package com.springboot.cloud.common.web.interceptor;
|
||||||
|
|
||||||
|
import com.springboot.cloud.common.core.util.UserContextHolder;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
|
|
||||||
|
public class UserInterceptorTest {
|
||||||
|
@Test
|
||||||
|
public void preHandle_当未设置token_user_那么正常处理下一个handle() throws Exception {
|
||||||
|
UserInterceptor userInterceptor = new UserInterceptor();
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
userInterceptor.preHandle(request, response, new Object());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void preHandle_当设置token的username_那么username可以在线程中拿出来用() throws Exception {
|
||||||
|
UserInterceptor userInterceptor = new UserInterceptor();
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.addHeader("x-client-token-user", "{\"user_name\":\"zhangsan\"}");
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
userInterceptor.preHandle(request, response, new Object());
|
||||||
|
Assert.assertEquals(UserContextHolder.getInstance().getUsername(), "zhangsan");
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
doc/连锁ERP架构.xmind
Normal file
BIN
doc/连锁ERP架构.xmind
Normal file
Binary file not shown.
BIN
doc/连锁ERP架构说明书.docx
Normal file
BIN
doc/连锁ERP架构说明书.docx
Normal file
Binary file not shown.
16
gateway/gateway-admin/.gitignore
vendored
Normal file
16
gateway/gateway-admin/.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
target/
|
||||||
|
logs/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
33
gateway/gateway-admin/pom.xml
Normal file
33
gateway/gateway-admin/pom.xml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>gateway-admin</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>gateway-admin</name>
|
||||||
|
<description>Demo Gateway Admin project for Spring Cloud</description>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<artifactId>webapp-parent</artifactId>
|
||||||
|
<groupId>business.chaoran</groupId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<relativePath>../../webapps/webapp-parent/pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alicp.jetcache</groupId>
|
||||||
|
<artifactId>jetcache-starter-redis</artifactId>
|
||||||
|
<version>2.5.14</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-gateway-core</artifactId>
|
||||||
|
<version>2.1.0.RELEASE</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
66
gateway/gateway-admin/src/main/db/db.sql
Normal file
66
gateway/gateway-admin/src/main/db/db.sql
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
SET NAMES utf8;
|
||||||
|
|
||||||
|
DROP DATABASE IF EXISTS sc_gateway;
|
||||||
|
CREATE DATABASE sc_gateway DEFAULT CHARSET utf8mb4;
|
||||||
|
|
||||||
|
USE sc_gateway;
|
||||||
|
|
||||||
|
-- 网关路由表
|
||||||
|
DROP TABLE IF EXISTS gateway_route;
|
||||||
|
CREATE TABLE gateway_route
|
||||||
|
(
|
||||||
|
id VARCHAR(20) PRIMARY KEY COMMENT 'id',
|
||||||
|
route_id VARCHAR(100) NOT NULL COMMENT '路由id',
|
||||||
|
uri VARCHAR(100) NOT NULL COMMENT 'uri路径',
|
||||||
|
predicates TEXT NOT NULL COMMENT '判定器',
|
||||||
|
filters TEXT COMMENT '过滤器',
|
||||||
|
orders INT COMMENT '排序',
|
||||||
|
description VARCHAR(500) COMMENT '描述',
|
||||||
|
status VARCHAR(1) DEFAULT 'Y' COMMENT '状态:Y-有效,N-无效',
|
||||||
|
created_time DATETIME NOT NULL DEFAULT now() COMMENT '创建时间',
|
||||||
|
updated_time DATETIME NOT NULL DEFAULT now() COMMENT '更新时间',
|
||||||
|
created_by VARCHAR(100) NOT NULL COMMENT '创建人',
|
||||||
|
updated_by VARCHAR(100) NOT NULL COMMENT '更新人'
|
||||||
|
) COMMENT '网关路由表';
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX ux_gateway_routes_uri ON gateway_route (uri);
|
||||||
|
|
||||||
|
|
||||||
|
-- DML初始数据
|
||||||
|
-- 路由数据
|
||||||
|
INSERT INTO gateway_route (id, route_id, uri, predicates, filters, orders, description, status, created_time, updated_time, created_by, updated_by)
|
||||||
|
VALUES
|
||||||
|
(101,
|
||||||
|
'authorization-server',
|
||||||
|
'lb://authorization-server:8000',
|
||||||
|
'[{"name":"Path","args":{"pattern":"/authorization-server/**"}}]',
|
||||||
|
'[{"name":"StripPrefix","args":{"parts":"1"}}]',
|
||||||
|
100,
|
||||||
|
'授权认证服务网关注册',
|
||||||
|
'Y', now(), now(), 'system', 'system'),
|
||||||
|
(102,
|
||||||
|
'authentication-server',
|
||||||
|
'lb://authentication-server:8001',
|
||||||
|
'[{"name":"Path","args":{"pattern":"/authentication-server/**"}}]',
|
||||||
|
'[{"name":"StripPrefix","args":{"parts":"1"}}]',
|
||||||
|
100,
|
||||||
|
'签权服务网关注册',
|
||||||
|
'Y', now(), now(), 'system', 'system'),
|
||||||
|
(103,
|
||||||
|
'organization',
|
||||||
|
'lb://organization:8010',
|
||||||
|
'[{"name":"Path","args":{"pattern":"/organization/**"}}]',
|
||||||
|
'[{"name":"StripPrefix","args":{"parts":"1"}}]',
|
||||||
|
100,
|
||||||
|
'系统管理相关接口',
|
||||||
|
'Y', now(), now(), 'system', 'system'),
|
||||||
|
(104,
|
||||||
|
'gateway-admin',
|
||||||
|
'lb://gateway-admin:8445',
|
||||||
|
'[{"name":"Path","args":{"pattern":"/gateway-admin/**"}}]',
|
||||||
|
'[{"name":"StripPrefix","args":{"parts":"1"}}]',
|
||||||
|
100,
|
||||||
|
'网关管理相关接口',
|
||||||
|
'Y', now(), now(), 'system', 'system')
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package com.springboot.cloud.gateway.admin;
|
||||||
|
|
||||||
|
import com.alicp.jetcache.anno.config.EnableCreateCacheAnnotation;
|
||||||
|
import com.alicp.jetcache.anno.config.EnableMethodCache;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
|
||||||
|
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||||
|
import org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration;
|
||||||
|
|
||||||
|
@SpringBootApplication(exclude = GatewayClassPathWarningAutoConfiguration.class)
|
||||||
|
@EnableDiscoveryClient
|
||||||
|
@EnableCircuitBreaker
|
||||||
|
@EnableMethodCache(basePackages = "com.springboot.cloud")
|
||||||
|
@EnableCreateCacheAnnotation
|
||||||
|
public class GatewayAdminApplication {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(GatewayAdminApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
package com.springboot.cloud.gateway.admin.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
|
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.amqp.core.Binding;
|
||||||
|
import org.springframework.amqp.core.BindingBuilder;
|
||||||
|
import org.springframework.amqp.core.Queue;
|
||||||
|
import org.springframework.amqp.core.TopicExchange;
|
||||||
|
import org.springframework.amqp.support.converter.ContentTypeDelegatingMessageConverter;
|
||||||
|
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||||
|
import org.springframework.amqp.support.converter.MessageConverter;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Slf4j
|
||||||
|
public class BusConfig {
|
||||||
|
|
||||||
|
public static final String QUEUE_NAME = "event-gateway";
|
||||||
|
public static final String EXCHANGE_NAME = "spring-boot-exchange";
|
||||||
|
public static final String ROUTING_KEY = "gateway-route";
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
Queue queue() {
|
||||||
|
log.info("queue name:{}", QUEUE_NAME);
|
||||||
|
return new Queue(QUEUE_NAME, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
TopicExchange exchange() {
|
||||||
|
log.info("exchange:{}", EXCHANGE_NAME);
|
||||||
|
return new TopicExchange(EXCHANGE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
Binding binding(Queue queue, TopicExchange exchange) {
|
||||||
|
log.info("binding {} to {} with {}", queue, exchange, ROUTING_KEY);
|
||||||
|
return BindingBuilder.bind(queue).to(exchange).with(ROUTING_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MessageConverter messageConverter() {
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||||
|
return new ContentTypeDelegatingMessageConverter(new Jackson2JsonMessageConverter(objectMapper));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.springboot.cloud.gateway.admin.config;
|
||||||
|
|
||||||
|
import com.springboot.cloud.common.web.handler.PoMetaObjectHandler;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MyMetaObjectHandler extends PoMetaObjectHandler {
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package com.springboot.cloud.gateway.admin.config;
|
||||||
|
|
||||||
|
import com.springboot.cloud.common.web.redis.RedisConfig;
|
||||||
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableCaching
|
||||||
|
public class MyRedisConfig extends RedisConfig {
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package com.springboot.cloud.gateway.admin.config;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初使化Mybatis审计字段自动赋值的interceptor
|
||||||
|
*/
|
||||||
|
@EnableTransactionManagement
|
||||||
|
@Configuration
|
||||||
|
public class MybatisConfig {
|
||||||
|
/**
|
||||||
|
* 分页插件
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public PaginationInterceptor paginationInterceptor() {
|
||||||
|
return new PaginationInterceptor();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package com.springboot.cloud.gateway.admin.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import springfox.documentation.builders.ApiInfoBuilder;
|
||||||
|
import springfox.documentation.builders.PathSelectors;
|
||||||
|
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||||
|
import springfox.documentation.service.ApiInfo;
|
||||||
|
import springfox.documentation.spi.DocumentationType;
|
||||||
|
import springfox.documentation.spring.web.plugins.Docket;
|
||||||
|
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableSwagger2
|
||||||
|
public class SwaggerConfig {
|
||||||
|
@Bean
|
||||||
|
public Docket createRestApi() {
|
||||||
|
return new Docket(DocumentationType.SWAGGER_2)
|
||||||
|
.apiInfo(apiInfo())
|
||||||
|
.select()
|
||||||
|
.apis(RequestHandlerSelectors.basePackage("com.springboot.cloud.gateway.admin"))
|
||||||
|
.paths(PathSelectors.any())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ApiInfo apiInfo() {
|
||||||
|
return new ApiInfoBuilder()
|
||||||
|
.title("网关管理api")
|
||||||
|
.description("网关管理")
|
||||||
|
.termsOfServiceUrl("https://github.com/zhoutaoo/SpringCloud")
|
||||||
|
.version("2.0")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package com.springboot.cloud.gateway.admin.config;
|
||||||
|
|
||||||
|
import com.springboot.cloud.common.web.interceptor.UserInterceptor;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class WebServerMvcConfigurerAdapter implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public HandlerInterceptor userInterceptor() {
|
||||||
|
return new UserInterceptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
|
registry.addInterceptor(userInterceptor());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.springboot.cloud.gateway.admin.dao;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.springboot.cloud.gateway.admin.entity.po.GatewayRoute;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
@Repository
|
||||||
|
public interface GatewayRouteMapper extends BaseMapper<GatewayRoute> {
|
||||||
|
}
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
package com.springboot.cloud.gateway.admin.entity.form;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.springboot.cloud.common.web.entity.form.BaseForm;
|
||||||
|
import com.springboot.cloud.gateway.admin.entity.po.FilterDefinition;
|
||||||
|
import com.springboot.cloud.gateway.admin.entity.po.GatewayRoute;
|
||||||
|
import com.springboot.cloud.gateway.admin.entity.po.PredicateDefinition;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ApiModel
|
||||||
|
@Data
|
||||||
|
@Slf4j
|
||||||
|
public class GatewayRouteForm extends BaseForm<GatewayRoute> {
|
||||||
|
|
||||||
|
@NotEmpty(message = "网关断言不能为空")
|
||||||
|
@ApiModelProperty(value = "网关断言")
|
||||||
|
private List<PredicateDefinition> predicates = new ArrayList<>();
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "网关过滤器信息")
|
||||||
|
private List<FilterDefinition> filters = new ArrayList<>();
|
||||||
|
|
||||||
|
@NotBlank(message = "uri不能为空")
|
||||||
|
@ApiModelProperty(value = "网关uri")
|
||||||
|
private String uri;
|
||||||
|
|
||||||
|
@NotBlank(message = "路由id不能为空")
|
||||||
|
@ApiModelProperty(value = "网关路由id")
|
||||||
|
private String routeId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "排序")
|
||||||
|
private Integer orders = 0;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "网关路由描述信息")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GatewayRoute toPo(Class<GatewayRoute> clazz) {
|
||||||
|
GatewayRoute gatewayRoute = new GatewayRoute();
|
||||||
|
BeanUtils.copyProperties(this, gatewayRoute);
|
||||||
|
try {
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
gatewayRoute.setFilters(objectMapper.writeValueAsString(this.getFilters()));
|
||||||
|
gatewayRoute.setPredicates(objectMapper.writeValueAsString(this.getPredicates()));
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
log.error("网关filter或predicates配置转换异常", e);
|
||||||
|
}
|
||||||
|
return gatewayRoute;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
package com.springboot.cloud.gateway.admin.entity.form;
|
||||||
|
|
||||||
|
import com.springboot.cloud.common.web.entity.form.BaseQueryForm;
|
||||||
|
import com.springboot.cloud.gateway.admin.entity.param.GatewayRouteQueryParam;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Past;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ApiModel
|
||||||
|
@Data
|
||||||
|
public class GatewayRouteQueryForm extends BaseQueryForm<GatewayRouteQueryParam> {
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "uri路径", required = true)
|
||||||
|
private String uri;
|
||||||
|
|
||||||
|
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
|
||||||
|
@Past(message = "查询开始时间必须小于当前日期")
|
||||||
|
@ApiModelProperty(value = "查询开始时间")
|
||||||
|
private Date createdTimeStart;
|
||||||
|
|
||||||
|
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
|
||||||
|
@Past(message = "查询结束时间必须小于当前日期")
|
||||||
|
@ApiModelProperty(value = "查询结束时间")
|
||||||
|
private Date createdTimeEnd;
|
||||||
|
}
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
package com.springboot.cloud.gateway.admin.entity.ov;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.springboot.cloud.common.web.entity.vo.BaseVo;
|
||||||
|
import com.springboot.cloud.gateway.admin.entity.po.FilterDefinition;
|
||||||
|
import com.springboot.cloud.gateway.admin.entity.po.GatewayRoute;
|
||||||
|
import com.springboot.cloud.gateway.admin.entity.po.PredicateDefinition;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Data
|
||||||
|
@Slf4j
|
||||||
|
public class GatewayRouteVo extends BaseVo {
|
||||||
|
private String id;
|
||||||
|
private String routeId;
|
||||||
|
private String description;
|
||||||
|
private String status;
|
||||||
|
private String uri;
|
||||||
|
private Integer orders;
|
||||||
|
private String createdBy;
|
||||||
|
private Date createdTime;
|
||||||
|
private String updatedBy;
|
||||||
|
private Date updatedTime;
|
||||||
|
private List<FilterDefinition> filters = new ArrayList<>();
|
||||||
|
private List<PredicateDefinition> predicates = new ArrayList<>();
|
||||||
|
|
||||||
|
public GatewayRouteVo(GatewayRoute gatewayRoute) {
|
||||||
|
this.id = gatewayRoute.getId();
|
||||||
|
this.routeId = gatewayRoute.getRouteId();
|
||||||
|
this.uri = gatewayRoute.getUri();
|
||||||
|
this.description = gatewayRoute.getDescription();
|
||||||
|
this.status = gatewayRoute.getStatus();
|
||||||
|
this.orders = gatewayRoute.getOrders();
|
||||||
|
this.createdBy = gatewayRoute.getCreatedBy();
|
||||||
|
this.createdTime = gatewayRoute.getCreatedTime();
|
||||||
|
this.updatedBy = gatewayRoute.getUpdatedBy();
|
||||||
|
this.updatedTime = gatewayRoute.getUpdatedTime();
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
try {
|
||||||
|
this.filters = objectMapper.readValue(gatewayRoute.getFilters(), new TypeReference<List<FilterDefinition>>() {
|
||||||
|
});
|
||||||
|
this.predicates = objectMapper.readValue(gatewayRoute.getPredicates(), new TypeReference<List<PredicateDefinition>>() {
|
||||||
|
});
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("网关路由对象转换失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package com.springboot.cloud.gateway.admin.entity.param;
|
||||||
|
|
||||||
|
import com.springboot.cloud.common.web.entity.param.BaseParam;
|
||||||
|
import com.springboot.cloud.gateway.admin.entity.po.GatewayRoute;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class GatewayRouteQueryParam extends BaseParam<GatewayRoute> {
|
||||||
|
private String uri;
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package com.springboot.cloud.gateway.admin.entity.po;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@EqualsAndHashCode
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class FilterDefinition {
|
||||||
|
private String name;
|
||||||
|
private Map<String, String> args = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user