Преглед изворни кода

feat: add zzbusiness-auth module

zhangjun пре 2 година
родитељ
комит
f98c4b034c
19 измењених фајлова са 1292 додато и 0 уклоњено
  1. 1 0
      pom.xml
  2. 90 0
      zzbusiness-auth/pom.xml
  3. 5 0
      zzbusiness-auth/src/Main.java
  4. 29 0
      zzbusiness-auth/src/main/java/org/zhongzheng/auth/AuthApplication.java
  5. 39 0
      zzbusiness-auth/src/main/java/org/zhongzheng/auth/config/RedisFactoryConfig.java
  6. 111 0
      zzbusiness-auth/src/main/java/org/zhongzheng/auth/controller/AuthController.java
  7. 44 0
      zzbusiness-auth/src/main/java/org/zhongzheng/auth/enums/ZhongZhengUserEnum.java
  8. 88 0
      zzbusiness-auth/src/main/java/org/zhongzheng/auth/granter/CaptchaTokenGranter.java
  9. 36 0
      zzbusiness-auth/src/main/java/org/zhongzheng/auth/granter/ITokenGranter.java
  10. 64 0
      zzbusiness-auth/src/main/java/org/zhongzheng/auth/granter/PasswordTokenGranter.java
  11. 58 0
      zzbusiness-auth/src/main/java/org/zhongzheng/auth/granter/RefreshTokenGranter.java
  12. 62 0
      zzbusiness-auth/src/main/java/org/zhongzheng/auth/granter/TokenGranterBuilder.java
  13. 31 0
      zzbusiness-auth/src/main/java/org/zhongzheng/auth/granter/TokenParameter.java
  14. 103 0
      zzbusiness-auth/src/main/java/org/zhongzheng/auth/utils/TokenUtil.java
  15. 31 0
      zzbusiness-auth/src/main/resources/application.yml
  16. 154 0
      zzbusiness-common/src/main/java/org/zhongzheng/common/secure/AesUtil.java
  17. 337 0
      zzbusiness-common/src/main/java/org/zhongzheng/common/secure/SecureUtil.java
  18. 3 0
      zzbusiness-common/src/main/java/org/zhongzheng/common/utils/SpringUtil.java
  19. 6 0
      zzbusiness-gateway/src/main/resources/application.yml

+ 1 - 0
pom.xml

@@ -15,6 +15,7 @@
         <module>zzbusiness-gateway</module>
         <module>zzbusiness-user</module>
         <module>zzbusiness-info</module>
+        <module>zzbusiness-auth</module>
     </modules>
 
     <!-- 统一管理版本管理 -->

+ 90 - 0
zzbusiness-auth/pom.xml

@@ -0,0 +1,90 @@
+<?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>
+
+    <parent>
+        <groupId>org.zhongzheng</groupId>
+        <artifactId>ZZBusiness</artifactId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>zhongzheng-auth</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <!-- spring session的依赖 -->
+        <dependency>
+            <groupId>org.springframework.session</groupId>
+            <artifactId>spring-session-data-redis</artifactId>
+        </dependency>
+
+        <!-- Captcha -->
+        <dependency>
+            <groupId>com.github.whvcse</groupId>
+            <artifactId>easy-captcha</artifactId>
+            <version>${captcha.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.zhongzheng</groupId>
+            <artifactId>zzbusiness-common</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+
+
+        <dependency>
+            <!--父POM中锁定了版本信息,只需引入G,A即可 -->
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+            <version>2.2.8.RELEASE</version>
+        </dependency>
+    </dependencies>
+
+    <packaging>jar</packaging>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <!--生成的jar中,不要包含pom.xml和pom.properties这两个文件-->
+                        <addMavenDescriptor>true</addMavenDescriptor>
+                        <manifest>
+                            <mainClass>org.zhongzheng.auth.AuthApplication</mainClass>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${springboot.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 5 - 0
zzbusiness-auth/src/Main.java

@@ -0,0 +1,5 @@
+public class Main {
+    public static void main(String[] args) {
+        System.out.println("Hello world!");
+    }
+}

+ 29 - 0
zzbusiness-auth/src/main/java/org/zhongzheng/auth/AuthApplication.java

@@ -0,0 +1,29 @@
+package org.zhongzheng.auth;
+
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.client.loadbalancer.LoadBalanced;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
+
+
+@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
+@EnableFeignClients
+@EnableDiscoveryClient  //开启服务注册发现功能
+@RestController
+@ComponentScan(basePackages = {"org.zhongzheng.common","org.zhongzheng.auth"})
+public class AuthApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(AuthApplication.class, args);
+    }
+
+}

+ 39 - 0
zzbusiness-auth/src/main/java/org/zhongzheng/auth/config/RedisFactoryConfig.java

@@ -0,0 +1,39 @@
+package org.zhongzheng.auth.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisPassword;
+import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
+import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
+import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+
+@Configuration
+@EnableRedisHttpSession
+public class RedisFactoryConfig {
+
+    @Value("${spring.redis.host}")
+    String  redisHost;
+    @Value("${spring.redis.port}")
+    String  redisPort;
+    @Value("${spring.redis.password}")
+    String  redisPwd;
+    @Value("${spring.redis.database}")
+    String  redisDB;
+
+    /**
+     * 创建Redis连接,默认是连接本地localhost:6379
+     */
+    @Bean
+    public  RedisConnectionFactory connectionFactory() {
+        RedisStandaloneConfiguration redisStandaloneConfiguration =
+                new RedisStandaloneConfiguration();
+        redisStandaloneConfiguration.setHostName(redisHost);
+        redisStandaloneConfiguration.setPort(Integer.valueOf(redisPort));
+        redisStandaloneConfiguration.setDatabase(Integer.valueOf(redisDB));
+        redisStandaloneConfiguration.setPassword(RedisPassword.of(redisPwd));
+
+        return new JedisConnectionFactory(redisStandaloneConfiguration);
+    }
+}

+ 111 - 0
zzbusiness-auth/src/main/java/org/zhongzheng/auth/controller/AuthController.java

@@ -0,0 +1,111 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.zhongzheng.auth.controller;
+
+import com.wf.captcha.SpecCaptcha;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+
+import lombok.AllArgsConstructor;
+
+import org.zhongzheng.auth.granter.ITokenGranter;
+import org.zhongzheng.auth.granter.TokenGranterBuilder;
+import org.zhongzheng.auth.granter.TokenParameter;
+import org.zhongzheng.auth.utils.TokenUtil;
+import org.zhongzheng.auth.config.RedisFactoryConfig;
+
+import org.springframework.data.redis.core.RedisTemplate;
+
+import org.zhongzheng.common.utils.CacheNames;
+import org.zhongzheng.common.secure.AuthInfo;
+import org.zhongzheng.common.utils.R;
+import org.zhongzheng.common.utils.Kv;
+import org.zhongzheng.common.utils.Func;
+import org.zhongzheng.common.utils.RedisUtil;
+import org.zhongzheng.common.utils.WebUtil;
+import org.zhongzheng.common.feignclient.zzbusinessuser.entity.UserInfo;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.PostConstruct;
+import java.io.UnsupportedEncodingException;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 认证模块
+ *
+ * @author Chill
+ */
+@RestController
+@AllArgsConstructor
+@Api(value = "用户授权认证", tags = "授权接口")
+public class AuthController  {
+
+	private RedisUtil redisUtil;
+
+	private RedisFactoryConfig  rdsFactoryConfig;
+
+	@PostConstruct
+	void afterConstruct(){
+		System.out.println(" ++++++++++++++++++ AuthController: setConnectionFactory ");
+		redisUtil.setConnectionFactory(rdsFactoryConfig.connectionFactory());
+	}
+
+	@PostMapping("token")
+	@ApiOperation(value = "获取认证token", notes = "传入租户ID:tenantId,账号:account,密码:password")
+	public R<AuthInfo> token(@ApiParam(value = "授权类型", required = true) @RequestParam(defaultValue = "password", required = false) String grantType,
+							 @ApiParam(value = "刷新令牌") @RequestParam(required = false) String refreshToken,
+							 @ApiParam(value = "租户ID", required = true) @RequestParam(defaultValue = "000000", required = false) String tenantId,
+							 @ApiParam(value = "账号") @RequestParam(required = false) String account,
+							 @ApiParam(value = "密码") @RequestParam(required = false) String password) throws UnsupportedEncodingException {
+
+		String userType = Func.toStr(WebUtil.getRequest().getHeader(TokenUtil.USER_TYPE_HEADER_KEY), TokenUtil.DEFAULT_USER_TYPE);
+
+		TokenParameter tokenParameter = new TokenParameter();
+		tokenParameter.getArgs().set("tenantId", tenantId)
+			.set("account", account)
+			.set("password", password)
+			.set("grantType", grantType)
+			.set("refreshToken", refreshToken)
+			.set("userType", userType);
+
+		ITokenGranter granter = TokenGranterBuilder.getGranter(grantType);
+		UserInfo userInfo = granter.grant(tokenParameter);
+
+		if (userInfo == null || userInfo.getUser() == null || userInfo.getUser().getId() == null) {
+			return R.fail(TokenUtil.USER_NOT_FOUND);
+		}
+
+		return R.data(TokenUtil.createAuthInfo(userInfo));
+	}
+
+	@GetMapping("/captcha")
+	@ApiOperation(value = "获取验证码")
+	public R<Kv> captcha() {
+		SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5);
+		String verCode = specCaptcha.text().toLowerCase();
+		String key = UUID.randomUUID().toString();
+		// 存入redis并设置过期时间为30分钟
+		redisUtil.set(CacheNames.CAPTCHA_KEY + key, verCode, 30L, TimeUnit.MINUTES);
+		// 将key和base64返回给前端
+		return R.data(Kv.init().set("key", key).set("image", specCaptcha.toBase64()));
+	}
+
+}

+ 44 - 0
zzbusiness-auth/src/main/java/org/zhongzheng/auth/enums/ZhongZhengUserEnum.java

@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.zhongzheng.auth.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 用户类型枚举
+ *
+ * @author Chill
+ */
+@Getter
+@AllArgsConstructor
+public enum ZhongZhengUserEnum {
+
+	/**
+	 * web
+	 */
+	WEB("web", 1),
+
+	/**
+	 * app
+	 */
+	APP("app", 2),
+	;
+
+	final String name;
+	final int category;
+
+}

+ 88 - 0
zzbusiness-auth/src/main/java/org/zhongzheng/auth/granter/CaptchaTokenGranter.java

@@ -0,0 +1,88 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.zhongzheng.auth.granter;
+
+import lombok.AllArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.zhongzheng.auth.enums.ZhongZhengUserEnum;
+import org.zhongzheng.auth.utils.TokenUtil;
+
+import org.springframework.data.redis.core.RedisTemplate;
+
+import org.zhongzheng.common.utils.CacheNames;
+import org.zhongzheng.common.utils.ServiceException;
+import org.zhongzheng.common.utils.R;
+import org.zhongzheng.common.utils.RedisUtil;
+import org.zhongzheng.common.utils.WebUtil;
+import org.zhongzheng.common.utils.DigestUtil;
+import org.zhongzheng.common.utils.StringUtil;
+import org.zhongzheng.common.utils.Func;
+
+import org.zhongzheng.common.feignclient.zzbusinessuser.entity.UserInfo;
+import org.zhongzheng.common.feignclient.zzbusinessuser.IUserClient;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 验证码TokenGranter
+ *
+ * @author Chill
+ */
+@Component
+@AllArgsConstructor
+public class CaptchaTokenGranter implements ITokenGranter {
+
+	public static final String GRANT_TYPE = "captcha";
+
+	private IUserClient userClient;
+	private RedisUtil redisUtil;
+
+	@Override
+	public UserInfo grant(TokenParameter tokenParameter) {
+		HttpServletRequest request = WebUtil.getRequest();
+
+		String key = request.getHeader(TokenUtil.CAPTCHA_HEADER_KEY);
+		String code = request.getHeader(TokenUtil.CAPTCHA_HEADER_CODE);
+		// 获取验证码
+		String redisCode = String.valueOf(redisUtil.get(CacheNames.CAPTCHA_KEY + key));
+		// 判断验证码
+		if (code == null || !StringUtil.equalsIgnoreCase(redisCode, code)) {
+			throw new ServiceException(TokenUtil.CAPTCHA_NOT_CORRECT);
+		}
+
+		String tenantId = tokenParameter.getArgs().getStr("tenantId");
+		String account = tokenParameter.getArgs().getStr("account");
+		String password = tokenParameter.getArgs().getStr("password");
+		UserInfo userInfo = null;
+		if (Func.isNoneBlank(account, password)) {
+			// 获取用户类型
+			String userType = tokenParameter.getArgs().getStr("userType");
+			R<UserInfo> result;
+			// 根据不同用户类型调用对应的接口返回数据,用户可自行拓展
+			if (userType.equals(ZhongZhengUserEnum.WEB.getName())) {
+				result = userClient.userInfo(tenantId, account, DigestUtil.encrypt(password));
+			} else if (userType.equals(ZhongZhengUserEnum.APP.getName())) {
+				result = userClient.userInfo(tenantId, account, DigestUtil.encrypt(password));
+			} else {
+				result = userClient.userInfo(tenantId, account, DigestUtil.encrypt(password));
+			}
+			userInfo = result.isSuccess() ? result.getData() : null;
+		}
+		return userInfo;
+	}
+
+}

+ 36 - 0
zzbusiness-auth/src/main/java/org/zhongzheng/auth/granter/ITokenGranter.java

@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.zhongzheng.auth.granter;
+
+
+import org.zhongzheng.common.feignclient.zzbusinessuser.entity.UserInfo;
+
+/**
+ * 授权认证统一接口.
+ *
+ * @author Chill
+ */
+public interface ITokenGranter {
+
+	/**
+	 * 获取用户信息
+	 *
+	 * @param tokenParameter 授权参数
+	 * @return UserInfo
+	 */
+	UserInfo grant(TokenParameter tokenParameter);
+
+}

+ 64 - 0
zzbusiness-auth/src/main/java/org/zhongzheng/auth/granter/PasswordTokenGranter.java

@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.zhongzheng.auth.granter;
+
+import lombok.AllArgsConstructor;
+import org.zhongzheng.auth.enums.ZhongZhengUserEnum;
+import org.zhongzheng.common.utils.R;
+import org.zhongzheng.common.utils.DigestUtil;
+import org.zhongzheng.common.utils.Func;
+import org.zhongzheng.common.feignclient.zzbusinessuser.entity.UserInfo;
+import org.zhongzheng.common.feignclient.zzbusinessuser.IUserClient;
+import org.springframework.stereotype.Component;
+import org.zhongzheng.auth.granter.ITokenGranter;
+
+/**
+ * PasswordTokenGranter
+ *
+ * @author Chill
+ */
+@Component
+@AllArgsConstructor
+public class PasswordTokenGranter implements ITokenGranter {
+
+	public static final String GRANT_TYPE = "password";
+
+	private IUserClient userClient;
+
+	@Override
+	public UserInfo grant(TokenParameter tokenParameter) {
+		String tenantId = tokenParameter.getArgs().getStr("tenantId");
+		String account = tokenParameter.getArgs().getStr("account");
+		String password = tokenParameter.getArgs().getStr("password");
+		UserInfo userInfo = null;
+		if (Func.isNoneBlank(account, password)) {
+			// 获取用户类型
+			String userType = tokenParameter.getArgs().getStr("userType");
+			R<UserInfo> result;
+			// 根据不同用户类型调用对应的接口返回数据,用户可自行拓展
+			if (userType.equals(ZhongZhengUserEnum.WEB.getName())) {
+				result = userClient.userInfo(tenantId, account, DigestUtil.encrypt(password));
+			} else if (userType.equals(ZhongZhengUserEnum.APP.getName())) {
+				result = userClient.userInfo(tenantId, account, DigestUtil.encrypt(password));
+			} else {
+				result = userClient.userInfo(tenantId, account, DigestUtil.encrypt(password));
+			}
+			userInfo = result.isSuccess() ? result.getData() : null;
+		}
+		return userInfo;
+	}
+
+}

+ 58 - 0
zzbusiness-auth/src/main/java/org/zhongzheng/auth/granter/RefreshTokenGranter.java

@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.zhongzheng.auth.granter;
+
+import io.jsonwebtoken.Claims;
+import lombok.AllArgsConstructor;
+import org.zhongzheng.common.env.TokenConstants;
+import org.zhongzheng.common.secure.SecureUtil;
+import org.zhongzheng.common.utils.R;
+import org.zhongzheng.common.utils.Func;
+import org.zhongzheng.common.feignclient.zzbusinessuser.entity.UserInfo;
+import org.zhongzheng.common.feignclient.zzbusinessuser.IUserClient;
+import org.springframework.stereotype.Component;
+
+import java.util.Objects;
+
+/**
+ * RefreshTokenGranter
+ *
+ * @author Chill
+ */
+@Component
+@AllArgsConstructor
+public class RefreshTokenGranter implements ITokenGranter {
+
+	public static final String GRANT_TYPE = "refresh_token";
+
+	private IUserClient userClient;
+
+	@Override
+	public UserInfo grant(TokenParameter tokenParameter) {
+		String grantType = tokenParameter.getArgs().getStr("grantType");
+		String refreshToken = tokenParameter.getArgs().getStr("refreshToken");
+		UserInfo userInfo = null;
+		if (Func.isNoneBlank(grantType, refreshToken) && grantType.equals(TokenConstants.REFRESH_TOKEN)) {
+			Claims claims = SecureUtil.parseJWT(refreshToken);
+			String tokenType = Func.toStr(Objects.requireNonNull(claims).get(TokenConstants.TOKEN_TYPE));
+			if (tokenType.equals(TokenConstants.REFRESH_TOKEN)) {
+				R<UserInfo> result = userClient.userInfo(Func.toLong(claims.get(TokenConstants.USER_ID)));
+				userInfo = result.isSuccess() ? result.getData() : null;
+			}
+		}
+		return userInfo;
+	}
+}

+ 62 - 0
zzbusiness-auth/src/main/java/org/zhongzheng/auth/granter/TokenGranterBuilder.java

@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.zhongzheng.auth.granter;
+
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.zhongzheng.common.secure.SecureException;
+import org.zhongzheng.common.utils.Func;
+import org.zhongzheng.common.utils.SpringUtil;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * TokenGranterBuilder
+ *
+ * @author Chill
+ */
+@Component
+@AllArgsConstructor
+public class TokenGranterBuilder {
+
+	/**
+	 * TokenGranter缓存池
+	 */
+	private static final Map<String, ITokenGranter> GRANTER_POOL = new ConcurrentHashMap<>();
+
+	static {
+		GRANTER_POOL.put(PasswordTokenGranter.GRANT_TYPE, SpringUtil.getBean(PasswordTokenGranter.class));
+		GRANTER_POOL.put(CaptchaTokenGranter.GRANT_TYPE, SpringUtil.getBean(CaptchaTokenGranter.class));
+		GRANTER_POOL.put(RefreshTokenGranter.GRANT_TYPE, SpringUtil.getBean(RefreshTokenGranter.class));
+	}
+
+	/**
+	 * 获取TokenGranter
+	 *
+	 * @param grantType 授权类型
+	 * @return ITokenGranter
+	 */
+	public static ITokenGranter getGranter(String grantType) {
+		ITokenGranter tokenGranter = GRANTER_POOL.get(Func.toStr(grantType, PasswordTokenGranter.GRANT_TYPE));
+		if (tokenGranter == null) {
+			throw new SecureException("no grantType was found");
+		} else {
+			return tokenGranter;
+		}
+	}
+
+}

+ 31 - 0
zzbusiness-auth/src/main/java/org/zhongzheng/auth/granter/TokenParameter.java

@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.zhongzheng.auth.granter;
+
+import lombok.Data;
+import org.zhongzheng.common.utils.Kv;
+
+/**
+ * TokenParameter
+ *
+ * @author Chill
+ */
+@Data
+public class TokenParameter {
+
+	private Kv args = Kv.init();
+
+}

+ 103 - 0
zzbusiness-auth/src/main/java/org/zhongzheng/auth/utils/TokenUtil.java

@@ -0,0 +1,103 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.zhongzheng.auth.utils;
+
+import java.io.UnsupportedEncodingException;
+
+import org.zhongzheng.common.secure.AuthInfo;
+import org.zhongzheng.common.secure.TokenInfo;
+import org.zhongzheng.common.secure.SecureUtil;
+import org.zhongzheng.common.utils.Func;
+import org.zhongzheng.common.feignclient.zzbusinessuser.entity.User;
+import org.zhongzheng.common.feignclient.zzbusinessuser.entity.UserInfo;
+
+import org.zhongzheng.common.env.TokenConstants;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 认证工具类
+ *
+ * @author Chill
+ */
+public class TokenUtil {
+
+	public final static String CAPTCHA_HEADER_KEY = "Captcha-Key";
+	public final static String CAPTCHA_HEADER_CODE = "Captcha-Code";
+	public final static String CAPTCHA_NOT_CORRECT = "验证码不正确";
+	public final static String TENANT_HEADER_KEY = "Tenant-Id";
+	public final static String DEFAULT_TENANT_ID = "000000";
+	public final static String USER_TYPE_HEADER_KEY = "User-Type";
+	public final static String DEFAULT_USER_TYPE = "web";
+	public final static String USER_NOT_FOUND = "用户名或密码错误";
+	public final static String HEADER_KEY = "Authorization";
+	public final static String HEADER_PREFIX = "Basic ";
+	public final static String DEFAULT_AVATAR = "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png";
+
+	/**
+	 * 创建认证token
+	 *
+	 * @param userInfo 用户信息
+	 * @return token
+	 */
+	public static AuthInfo createAuthInfo(UserInfo userInfo) throws UnsupportedEncodingException{
+		User user = userInfo.getUser();
+
+		//设置jwt参数
+		Map<String, String> param = new HashMap<>(16);
+		param.put(TokenConstants.TOKEN_TYPE, TokenConstants.ACCESS_TOKEN);
+		param.put(TokenConstants.TENANT_ID, user.getTenantId());
+		param.put(TokenConstants.OAUTH_ID, userInfo.getOauthId());
+		param.put(TokenConstants.USER_ID, Func.toStr(user.getId()));
+		param.put(TokenConstants.ROLE_ID, user.getRoleId());
+		param.put(TokenConstants.DEPT_ID, user.getDeptId());
+		param.put(TokenConstants.ACCOUNT, user.getAccount());
+		param.put(TokenConstants.USER_NAME, user.getAccount());
+		param.put(TokenConstants.ROLE_NAME, Func.join(userInfo.getRoles()));
+
+		TokenInfo accessToken = SecureUtil.createJWT(param, "audience", "issuser", TokenConstants.ACCESS_TOKEN);
+		AuthInfo authInfo = new AuthInfo();
+		authInfo.setUserId(user.getId());
+		authInfo.setTenantId(user.getTenantId());
+		authInfo.setOauthId(userInfo.getOauthId());
+		authInfo.setAccount(user.getAccount());
+		authInfo.setUserName(user.getRealName());
+		authInfo.setAuthority(Func.join(userInfo.getRoles()));
+		authInfo.setAccessToken(accessToken.getToken());
+		authInfo.setExpiresIn(accessToken.getExpire());
+		authInfo.setRefreshToken(createRefreshToken(userInfo).getToken());
+		authInfo.setTokenType(TokenConstants.BEARER);
+		authInfo.setLicense(TokenConstants.LICENSE_NAME);
+
+		return authInfo;
+	}
+
+	/**
+	 * 创建refreshToken
+	 *
+	 * @param userInfo 用户信息
+	 * @return refreshToken
+	 */
+	private static TokenInfo createRefreshToken(UserInfo userInfo) throws UnsupportedEncodingException {
+		User user = userInfo.getUser();
+		Map<String, String> param = new HashMap<>(16);
+		param.put(TokenConstants.TOKEN_TYPE, TokenConstants.REFRESH_TOKEN);
+		param.put(TokenConstants.USER_ID, Func.toStr(user.getId()));
+		return SecureUtil.createJWT(param, "audience", "issuser", TokenConstants.REFRESH_TOKEN);
+	}
+
+}

+ 31 - 0
zzbusiness-auth/src/main/resources/application.yml

@@ -0,0 +1,31 @@
+server:
+  port: 9005
+
+spring:
+  application:
+    name: ZZBUSINESS-AUTH
+  main:
+    allow-bean-definition-overriding: true
+  cloud:
+    nacos:
+      discovery:
+        server-addr: localhost:8848
+
+  redis:
+    # 地址
+    host: 192.168.1.222
+    # 端口,默认为6379
+    port: 6379
+    # 数据库索引
+    database: 6
+    # 密码
+    password: zhongzheng2021_redis
+    # 连接超时时间
+    timeout: 5s
+
+  jedis:
+    pool:
+      max-active: 8
+      max-wait: 1ms
+      max-idle: 4
+      min-idle: 0

+ 154 - 0
zzbusiness-common/src/main/java/org/zhongzheng/common/secure/AesUtil.java

@@ -0,0 +1,154 @@
+package org.zhongzheng.common.secure;
+
+//
+// Source code recreated from a .class file by IntelliJ IDEA
+// (powered by FernFlower decompiler)
+//
+
+
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.Objects;
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+import org.zhongzheng.common.utils.*;
+
+public class AesUtil {
+    public static final Charset DEFAULT_CHARSET;
+
+    public AesUtil() {
+    }
+
+    public static String genAesKey() {
+        return StringUtil.random(32);
+    }
+
+    public static byte[] encrypt(String content, String aesTextKey) {
+        return encrypt(content.getBytes(DEFAULT_CHARSET), aesTextKey);
+    }
+
+    public static byte[] encrypt(String content, Charset charset, String aesTextKey) {
+        return encrypt(content.getBytes(charset), aesTextKey);
+    }
+
+    public static byte[] encrypt(byte[] content, String aesTextKey) {
+        return encrypt(content, ((String)Objects.requireNonNull(aesTextKey)).getBytes(DEFAULT_CHARSET));
+    }
+
+    public static String encryptToHex(String content, String aesTextKey) {
+        return HexUtil.encodeToString(encrypt(content, aesTextKey));
+    }
+
+    public static String encryptToHex(byte[] content, String aesTextKey) {
+        return HexUtil.encodeToString(encrypt(content, aesTextKey));
+    }
+
+    public static String encryptToBase64(String content, String aesTextKey) {
+        return Base64Util.encodeToString(encrypt(content, aesTextKey));
+    }
+
+    public static String encryptToBase64(byte[] content, String aesTextKey) {
+        return Base64Util.encodeToString(encrypt(content, aesTextKey));
+    }
+
+    @Nullable
+    public static String decryptFormHexToString(@Nullable String content, String aesTextKey) {
+        byte[] hexBytes = decryptFormHex(content, aesTextKey);
+        return hexBytes == null ? null : new String(hexBytes, DEFAULT_CHARSET);
+    }
+
+    @Nullable
+    public static byte[] decryptFormHex(@Nullable String content, String aesTextKey) {
+        return StringUtil.isBlank(content) ? null : decryptFormHex(content.getBytes(DEFAULT_CHARSET), aesTextKey);
+    }
+
+    public static byte[] decryptFormHex(byte[] content, String aesTextKey) {
+        return decrypt(HexUtil.decode(content), aesTextKey);
+    }
+
+    @Nullable
+    public static String decryptFormBase64ToString(@Nullable String content, String aesTextKey) {
+        byte[] hexBytes = decryptFormBase64(content, aesTextKey);
+        return hexBytes == null ? null : new String(hexBytes, DEFAULT_CHARSET);
+    }
+
+    @Nullable
+    public static byte[] decryptFormBase64(@Nullable String content, String aesTextKey) {
+        return StringUtil.isBlank(content) ? null : decryptFormBase64(content.getBytes(DEFAULT_CHARSET), aesTextKey);
+    }
+
+    public static byte[] decryptFormBase64(byte[] content, String aesTextKey) {
+        return decrypt(Base64Util.decode(content), aesTextKey);
+    }
+
+    public static String decryptToString(byte[] content, String aesTextKey) {
+        return new String(decrypt(content, aesTextKey), DEFAULT_CHARSET);
+    }
+
+    public static byte[] decrypt(byte[] content, String aesTextKey) {
+        return decrypt(content, ((String)Objects.requireNonNull(aesTextKey)).getBytes(DEFAULT_CHARSET));
+    }
+
+    public static byte[] encrypt(byte[] content, byte[] aesKey) {
+        return aes(AesUtil.Pkcs7Encoder.encode(content), aesKey, 1);
+    }
+
+    public static byte[] decrypt(byte[] encrypted, byte[] aesKey) {
+        return AesUtil.Pkcs7Encoder.decode(aes(encrypted, aesKey, 2));
+    }
+
+    private static byte[] aes(byte[] encrypted, byte[] aesKey, int mode) {
+        Assert.isTrue(aesKey.length == 32, "IllegalAesKey, aesKey's length must be 32");
+
+        try {
+            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
+            SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
+            IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
+            cipher.init(mode, keySpec, iv);
+            return cipher.doFinal(encrypted);
+        } catch (Exception var6) {
+            throw Exceptions.unchecked(var6);
+        }
+    }
+
+    static {
+        DEFAULT_CHARSET = Charsets.UTF_8;
+    }
+
+    private static class Pkcs7Encoder {
+        private static final int BLOCK_SIZE = 32;
+
+        private Pkcs7Encoder() {
+        }
+
+        private static byte[] encode(byte[] src) {
+            int count = src.length;
+            int amountToPad = 32 - count % 32;
+            byte pad = (byte)(amountToPad & 255);
+            byte[] pads = new byte[amountToPad];
+
+            int length;
+            for(length = 0; length < amountToPad; ++length) {
+                pads[length] = pad;
+            }
+
+            length = count + amountToPad;
+            byte[] dest = new byte[length];
+            System.arraycopy(src, 0, dest, 0, count);
+            System.arraycopy(pads, 0, dest, count, amountToPad);
+            return dest;
+        }
+
+        private static byte[] decode(byte[] decrypted) {
+            int pad = decrypted[decrypted.length - 1];
+            if (pad < 1 || pad > 32) {
+                pad = 0;
+            }
+
+            return pad > 0 ? Arrays.copyOfRange(decrypted, 0, decrypted.length - pad) : decrypted;
+        }
+    }
+}

+ 337 - 0
zzbusiness-common/src/main/java/org/zhongzheng/common/secure/SecureUtil.java

@@ -0,0 +1,337 @@
+package org.zhongzheng.common.secure;
+
+//
+// Source code recreated from a .class file by IntelliJ IDEA
+// (powered by FernFlower decompiler)
+//
+
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.JwtBuilder;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+
+import java.io.UnsupportedEncodingException;
+import java.security.Key;
+import java.util.Base64;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Map;
+import java.util.Objects;
+import javax.crypto.spec.SecretKeySpec;
+import javax.servlet.http.HttpServletRequest;
+import org.zhongzheng.common.env.TokenConstants;
+import org.zhongzheng.common.secure.ZhongZhengUser;
+import org.zhongzheng.common.secure.TokenInfo;
+import org.zhongzheng.common.secure.SecureException;
+import org.zhongzheng.common.secure.ZhongZhengTokenProperties;
+import org.zhongzheng.common.secure.IClientDetails;
+import org.zhongzheng.common.secure.IClientDetailsService;
+import org.zhongzheng.common.secure.AesUtil;
+import org.zhongzheng.common.utils.Charsets;
+import org.zhongzheng.common.utils.Func;
+import org.zhongzheng.common.utils.SpringUtil;
+import org.zhongzheng.common.utils.StringUtil;
+import org.zhongzheng.common.utils.WebUtil;
+
+public class SecureUtil {
+    private static final String ZHONGZHENG_USER_REQUEST_ATTR = "_ZHONGZHENG_USER_REQUEST_ATTR_";
+    private static final String HEADER = "blade-auth";
+    private static final String BEARER = "bearer";
+    private static final String CRYPTO = "crypto";
+    private static final String ACCOUNT = "account";
+    private static final String USER_ID = "user_id";
+    private static final String ROLE_ID = "role_id";
+    private static final String DEPT_ID = "dept_id";
+    private static final String USER_NAME = "user_name";
+    private static final String ROLE_NAME = "role_name";
+    private static final String TENANT_ID = "tenant_id";
+    private static final String CLIENT_ID = "client_id";
+    private static final Integer AUTH_LENGTH;
+    private static IClientDetailsService CLIENT_DETAILS_SERVICE;
+    private static ZhongZhengTokenProperties TOKEN_PROPERTIES;
+    private static String BASE64_SECURITY;
+
+    public SecureUtil() {
+    }
+
+    private static IClientDetailsService getClientDetailsService() {
+        if (CLIENT_DETAILS_SERVICE == null) {
+            CLIENT_DETAILS_SERVICE = (IClientDetailsService)SpringUtil.getBean(IClientDetailsService.class);
+        }
+
+        return CLIENT_DETAILS_SERVICE;
+    }
+
+    private static ZhongZhengTokenProperties getTokenProperties() {
+        if (TOKEN_PROPERTIES == null) {
+            TOKEN_PROPERTIES = (ZhongZhengTokenProperties)SpringUtil.getBean(ZhongZhengTokenProperties.class);
+        }
+
+        return TOKEN_PROPERTIES;
+    }
+
+    private static String getBase64Security() {
+        if (BASE64_SECURITY == null) {
+            BASE64_SECURITY = Base64.getEncoder().encodeToString(getTokenProperties().getSignKey().getBytes(Charsets.UTF_8));
+        }
+
+        return BASE64_SECURITY;
+    }
+
+    public static ZhongZhengUser getUser() {
+        HttpServletRequest request = WebUtil.getRequest();
+        if (request == null) {
+            return null;
+        } else {
+            Object zzUser = request.getAttribute("_ZHONGZHENG_USER_REQUEST_ATTR_");
+            if (zzUser == null) {
+                zzUser = getUser(request);
+                if (zzUser != null) {
+                    request.setAttribute("_ZHONGZHENG_USER_REQUEST_ATTR_", zzUser);
+                }
+            }
+
+            return (ZhongZhengUser)zzUser;
+        }
+    }
+
+    public static ZhongZhengUser getUser(HttpServletRequest request) {
+        Claims claims = getClaims(request);
+        if (claims == null) {
+            return null;
+        } else {
+            String clientId = Func.toStr(claims.get("client_id"));
+            Long userId = Func.toLong(claims.get("user_id"));
+            String tenantId = Func.toStr(claims.get("tenant_id"));
+            String roleId = Func.toStr(claims.get("role_id"));
+            String deptId = Func.toStr(claims.get("dept_id"));
+            String account = Func.toStr(claims.get("account"));
+            String roleName = Func.toStr(claims.get("role_name"));
+            String userName = Func.toStr(claims.get("user_name"));
+            ZhongZhengUser zzUser = new ZhongZhengUser();
+            zzUser.setClientId(clientId);
+            zzUser.setUserId(userId);
+            zzUser.setTenantId(tenantId);
+            zzUser.setAccount(account);
+            zzUser.setRoleId(roleId);
+            zzUser.setDeptId(deptId);
+            zzUser.setRoleName(roleName);
+            zzUser.setUserName(userName);
+            return zzUser;
+        }
+    }
+
+    public static boolean isAdministrator() {
+        return StringUtil.containsAny(getUserRole(), new CharSequence[]{"administrator"});
+    }
+
+    public static Long getUserId() {
+        ZhongZhengUser user = getUser();
+        return null == user ? -1L : user.getUserId();
+    }
+
+    public static Long getUserId(HttpServletRequest request) {
+        ZhongZhengUser user = getUser(request);
+        return null == user ? -1L : user.getUserId();
+    }
+
+    public static String getUserAccount() {
+        ZhongZhengUser user = getUser();
+        return null == user ? "" : user.getAccount();
+    }
+
+    public static String getUserAccount(HttpServletRequest request) {
+        ZhongZhengUser user = getUser(request);
+        return null == user ? "" : user.getAccount();
+    }
+
+    public static String getUserName() {
+        ZhongZhengUser user = getUser();
+        return null == user ? "" : user.getUserName();
+    }
+
+    public static String getUserName(HttpServletRequest request) {
+        ZhongZhengUser user = getUser(request);
+        return null == user ? "" : user.getUserName();
+    }
+
+    public static String getUserRole() {
+        ZhongZhengUser user = getUser();
+        return null == user ? "" : user.getRoleName();
+    }
+
+    public static String getUserRole(HttpServletRequest request) {
+        ZhongZhengUser user = getUser(request);
+        return null == user ? "" : user.getRoleName();
+    }
+
+    public static String getTenantId() {
+        ZhongZhengUser user = getUser();
+        return null == user ? "" : user.getTenantId();
+    }
+
+    public static String getTenantId(HttpServletRequest request) {
+        ZhongZhengUser user = getUser(request);
+        return null == user ? "" : user.getTenantId();
+    }
+
+    public static String getClientId() {
+        ZhongZhengUser user = getUser();
+        return null == user ? "" : user.getClientId();
+    }
+
+    public static String getClientId(HttpServletRequest request) {
+        ZhongZhengUser user = getUser(request);
+        return null == user ? "" : user.getClientId();
+    }
+
+    public static Claims getClaims(HttpServletRequest request) {
+        String auth = request.getHeader("blade-auth");
+        String token = getToken(StringUtil.isNotBlank(auth) ? auth : request.getParameter("blade-auth"));
+        return parseJWT(token);
+    }
+
+    public static String getToken(String auth) {
+        if (isBearer(auth)) {
+            return auth.substring(AUTH_LENGTH);
+        } else {
+            return isCrypto(auth) ? AesUtil.decryptFormBase64ToString(auth.substring(AUTH_LENGTH), getTokenProperties().getAesKey()) : null;
+        }
+    }
+
+    public static Boolean isBearer(String auth) {
+        if (auth != null && auth.length() > AUTH_LENGTH) {
+            String headStr = auth.substring(0, 6).toLowerCase();
+            return headStr.compareTo("bearer") == 0;
+        } else {
+            return false;
+        }
+    }
+
+    public static Boolean isCrypto(String auth) {
+        if (auth != null && auth.length() > AUTH_LENGTH) {
+            String headStr = auth.substring(0, 6).toLowerCase();
+            return headStr.compareTo("crypto") == 0;
+        } else {
+            return false;
+        }
+    }
+
+    public static String getHeader() {
+        return getHeader((HttpServletRequest)Objects.requireNonNull(WebUtil.getRequest()));
+    }
+
+    public static String getHeader(HttpServletRequest request) {
+        return request.getHeader("blade-auth");
+    }
+
+    public static Claims parseJWT(String jsonWebToken) {
+        try {
+            return (Claims)Jwts.parserBuilder().setSigningKey(Base64.getDecoder().decode(getBase64Security())).build().parseClaimsJws(jsonWebToken).getBody();
+        } catch (Exception var2) {
+            return null;
+        }
+    }
+
+    public static TokenInfo createJWT(Map<String, String> user, String audience, String issuer, String tokenType) throws UnsupportedEncodingException {
+        String[] tokens = extractAndDecodeHeader();
+
+        assert tokens.length == 2;
+
+        String clientId = tokens[0];
+        String clientSecret = tokens[1];
+        IClientDetails clientDetails = clientDetails(clientId);
+        if (!validateClient(clientDetails, clientId, clientSecret)) {
+            throw new SecureException("客户端认证失败!");
+        } else {
+            SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
+            long nowMillis = System.currentTimeMillis();
+            Date now = new Date(nowMillis);
+            byte[] apiKeySecretBytes = Base64.getDecoder().decode(getBase64Security());
+            Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
+            JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JWT").setIssuer(issuer).setAudience(audience).signWith(signingKey);
+            user.forEach(builder::claim);
+            builder.claim("client_id", clientId);
+            long expireMillis;
+            if (tokenType.equals("access_token")) {
+                expireMillis = (long)(clientDetails.getAccessTokenValidity() * 1000);
+            } else if (tokenType.equals("refresh_token")) {
+                expireMillis = (long)(clientDetails.getRefreshTokenValidity() * 1000);
+            } else {
+                expireMillis = getExpire();
+            }
+
+            long expMillis = nowMillis + expireMillis;
+            Date exp = new Date(expMillis);
+            builder.setExpiration(exp).setNotBefore(now);
+            TokenInfo tokenInfo = new TokenInfo();
+            tokenInfo.setToken(builder.compact());
+            tokenInfo.setExpire((int)expireMillis / 1000);
+            return tokenInfo;
+        }
+    }
+
+    public static long getExpire() {
+        Calendar cal = Calendar.getInstance();
+        cal.add(6, 1);
+        cal.set(11, 3);
+        cal.set(13, 0);
+        cal.set(12, 0);
+        cal.set(14, 0);
+        return cal.getTimeInMillis() - System.currentTimeMillis();
+    }
+
+    public static String[] extractAndDecodeHeader() throws UnsupportedEncodingException {
+        try {
+            String header = ((HttpServletRequest)Objects.requireNonNull(WebUtil.getRequest())).getHeader("Authorization");
+            header = Func.toStr(header).replace("Basic%20", "Basic ");
+            if (!header.startsWith("Basic ")) {
+                throw new SecureException("No client information in request header");
+            } else {
+                byte[] base64Token = header.substring(6).getBytes(Charsets.UTF_8_NAME);
+
+                byte[] decoded;
+                try {
+                    decoded = Base64.getDecoder().decode(base64Token);
+                } catch (IllegalArgumentException var5) {
+                    throw new RuntimeException("Failed to decode basic authentication token");
+                }
+
+                String token = new String(decoded, Charsets.UTF_8_NAME);
+                int index = token.indexOf(":");
+                if (index == -1) {
+                    throw new RuntimeException("Invalid basic authentication token");
+                } else {
+                    return new String[]{token.substring(0, index), token.substring(index + 1)};
+                }
+            }
+        } catch (Throwable var6) {
+            throw var6;
+        }
+    }
+
+    public static String getClientIdFromHeader() throws UnsupportedEncodingException {
+        String[] tokens = extractAndDecodeHeader();
+
+        assert tokens.length == 2;
+
+        return tokens[0];
+    }
+
+    private static IClientDetails clientDetails(String clientId) {
+        return getClientDetailsService().loadClientByClientId(clientId);
+    }
+
+    private static boolean validateClient(IClientDetails clientDetails, String clientId, String clientSecret) {
+        if (clientDetails == null) {
+            return false;
+        } else {
+            return StringUtil.equals(clientId, clientDetails.getClientId()) && StringUtil.equals(clientSecret, clientDetails.getClientSecret());
+        }
+    }
+
+    static {
+        AUTH_LENGTH = TokenConstants.AUTH_LENGTH;
+    }
+}

+ 3 - 0
zzbusiness-common/src/main/java/org/zhongzheng/common/utils/SpringUtil.java

@@ -12,7 +12,9 @@ import org.springframework.beans.BeansException;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
 import org.springframework.context.ApplicationEvent;
+import org.springframework.stereotype.Component;
 
+@Component
 public class SpringUtil implements ApplicationContextAware {
     private static final Logger log = LoggerFactory.getLogger(SpringUtil.class);
     private static ApplicationContext context;
@@ -20,6 +22,7 @@ public class SpringUtil implements ApplicationContextAware {
     public SpringUtil() {
     }
 
+    @Override
     public void setApplicationContext(ApplicationContext context) throws BeansException {
         SpringUtil.context = context;
     }

+ 6 - 0
zzbusiness-gateway/src/main/resources/application.yml

@@ -27,6 +27,12 @@ spring:
           - Path=/zzbusiness-user/**    #服务访问路径
         filters:
           - StripPrefix=1    #请求转发的时候会去掉 /serv-con访问路径
+      - id: zzbusiness-auth #指定服务名
+        uri: lb://ZZBUSINESS-AUTH #去注册中心找这个服务名
+        predicates:    #断言,匹配访问的路径
+          - Path=/token/**, /captcha/**    #服务访问路径
+        filters:
+          - StripPrefix=0    #请求转发的时候会去掉 /serv-con访问路径
       globalcors: #跨域配置
         cors-configurations:
           '[/**]':