Pre Merge pull request !7 from nocodery/master

pull/7/MERGE
nocodery 5 years ago committed by Gitee
commit 1ddb34b028
  1. 3
      pom.xml
  2. 10
      sop-gateway/pom.xml
  3. 33
      sop-gateway/src/main/java/com/gitee/sop/gateway/config/MyConfig.java
  4. 12
      sop-gateway/src/main/resources/application-dev.properties
  5. 132
      sop-openapi/pom.xml
  6. 14
      sop-openapi/src/main/java/com/gitee/sop/openapi/SopOpenapiApplication.java
  7. 41
      sop-openapi/src/main/java/com/gitee/sop/openapi/config/ApolloApplicationConfig.java
  8. 33
      sop-openapi/src/main/java/com/gitee/sop/openapi/config/OpenapiConfig.java
  9. 43
      sop-openapi/src/main/java/com/gitee/sop/openapi/config/RedisConfig.java
  10. 80
      sop-openapi/src/main/java/com/gitee/sop/openapi/config/RedisOperator.java
  11. 68
      sop-openapi/src/main/java/com/gitee/sop/openapi/controller/AuthCodeController.java
  12. 27
      sop-openapi/src/main/java/com/gitee/sop/openapi/controller/AuthTokenController.java
  13. 61
      sop-openapi/src/main/java/com/gitee/sop/openapi/entity/BaseEntity.java
  14. 42
      sop-openapi/src/main/java/com/gitee/sop/openapi/entity/IsvAuthTokenEntity.java
  15. 33
      sop-openapi/src/main/java/com/gitee/sop/openapi/entity/IsvInfoEntity.java
  16. 26
      sop-openapi/src/main/java/com/gitee/sop/openapi/po/AuthCodePo.java
  17. 45
      sop-openapi/src/main/java/com/gitee/sop/openapi/po/AuthTokePo.java
  18. 17
      sop-openapi/src/main/java/com/gitee/sop/openapi/repository/IsvAuthTokenRepository.java
  19. 14
      sop-openapi/src/main/java/com/gitee/sop/openapi/repository/IsvInfoRepository.java
  20. 106
      sop-openapi/src/main/java/com/gitee/sop/openapi/service/AuthCodeService.java
  21. 20
      sop-openapi/src/main/java/com/gitee/sop/openapi/service/AuthService.java
  22. 83
      sop-openapi/src/main/java/com/gitee/sop/openapi/service/AuthTokenService.java
  23. 10
      sop-openapi/src/main/java/com/gitee/sop/openapi/util/AuthErrorEnum.java
  24. 23
      sop-openapi/src/main/java/com/gitee/sop/openapi/util/GrantType.java
  25. 27
      sop-openapi/src/main/java/com/gitee/sop/openapi/util/LogFormatUtil.java
  26. 53
      sop-openapi/src/main/java/com/gitee/sop/openapi/util/MD5GeneratorUtil.java
  27. 30
      sop-openapi/src/main/java/com/gitee/sop/openapi/util/RedisKeyUtil.java
  28. 9
      sop-openapi/src/main/java/com/gitee/sop/openapi/vo/AuthCodeVo.java
  29. 40
      sop-openapi/src/main/java/com/gitee/sop/openapi/vo/AuthTokenVo.java
  30. 2
      sop-openapi/src/main/resources/META-INF/app.properties
  31. 16
      sop-openapi/src/main/resources/application-cluster.yml
  32. 16
      sop-openapi/src/main/resources/application-sentinel.yml
  33. 13
      sop-openapi/src/main/resources/application-standalone.yml
  34. 37
      sop-openapi/src/main/resources/application.properties
  35. 4
      sop-openapi/src/main/resources/isp/auth_error_en.properties
  36. 9
      sop-openapi/src/main/resources/isp/auth_error_zh_CN.properties
  37. 5
      sop-openapi/src/main/resources/isp/bizerror_en.properties
  38. 9
      sop-openapi/src/main/resources/isp/bizerror_zh_CN.properties
  39. 20
      sop-openapi/src/test/java/com/gitee/sop/AppTest.java

@ -26,7 +26,8 @@
<module>sop-test</module>
<module>sop-sdk</module>
<module>sop-website</module>
</modules>
<module>sop-openapi</module>
</modules>
<properties>
<java.version>1.8</java.version>

@ -67,6 +67,16 @@
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redis 依赖commons-pool -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>

@ -1,23 +1,48 @@
package com.gitee.sop.gateway.config;
import com.alibaba.fastjson.JSONObject;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Slf4j
@Component
public class MyConfig {
@Autowired
private StringRedisTemplate redisTemplate;
private static String APP_AUTH_TOKEN_PREFIX = "com.gitee.sop.oauth2_access_token:";
public static String getAccessTokenKey(String accessToken) {
return APP_AUTH_TOKEN_PREFIX + accessToken;
}
@PostConstruct
public void after() {
ApiConfig.getInstance().setTokenValidator(apiParam -> {
// 获取客户端传递过来的token
String token = apiParam.fetchAccessToken();
return !StringUtils.isBlank(token);
// TODO: 校验token有效性,可以从redis中读取
try {
String token = apiParam.fetchAccessToken();
if (StringUtils.isBlank(token)) {
return Boolean.FALSE;
}
String json = redisTemplate.opsForValue().get(getAccessTokenKey(token));
if(StringUtils.isEmpty(json)) {
return Boolean.FALSE;
}
// 返回true表示这个token真实、有效
JSONObject userInfo = JSONObject.parseObject(json);
return userInfo != null;
} catch (Exception e) {
log.error("redisError", e);
return Boolean.FALSE;
}
});
}
}

@ -13,4 +13,14 @@ register.url=127.0.0.1:8848
# 如果需要传输大文本可以把值设置高一点
spring.codec.max-in-memory-size=262144
logging.level.com.gitee=debug
logging.level.com.gitee=debug
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.jedis.pool.min-idle=1
spring.redis.jedis.pool.max-idle=5
spring.redis.jedis.pool.max-wait=-1
spring.redis.jedis.pool.max-active=8
spring.redis.timeout=60000
spring.redis.database=0
spring.redis.password=

@ -0,0 +1,132 @@
<?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>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>
<groupId>com.gitee.sop.openapi</groupId>
<artifactId>sop-openapi</artifactId>
<name>sop-openapi</name>
<properties>
<apollo.version>1.1.0</apollo.version>
</properties>
<dependencies>
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId>
<version>4.1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 使用nacos注册中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redis 依赖commons-pool -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>net.oschina.durcframework</groupId>
<artifactId>fastmybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>${apollo.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 打包时跳过测试 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>

@ -0,0 +1,14 @@
package com.gitee.sop.openapi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class SopOpenapiApplication {
public static void main(String[] args) {
SpringApplication.run(SopOpenapiApplication.class, args);
}
}

@ -0,0 +1,41 @@
package com.gitee.sop.openapi.config;
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/***
*
* @author wangping created on 2020/11/5 16:06
*/
@Slf4j
@Component
public class ApolloApplicationConfig implements BeanPostProcessor {
@PostConstruct
public void init() {
loadApplication();
}
private void loadApplication() {
Config config = ConfigService.getAppConfig();
config.addChangeListener(this::getChangedEvent);
}
private void getChangedEvent(ConfigChangeEvent changeEvent) {
for (String key : changeEvent.changedKeys()) {
ConfigChange change = changeEvent.getChange(key);
log.info("Found change - key: {}, oldValue: {}, newValue: {}, changeType:{}",
change.getPropertyName(), change.getOldValue(),
change.getNewValue(), change.getChangeType());
}
}
}

@ -0,0 +1,33 @@
package com.gitee.sop.openapi.config;
import com.gitee.sop.servercommon.bean.ServiceConfig;
import com.gitee.sop.servercommon.swagger.SwaggerSupport;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
public class OpenapiConfig {
static {
ServiceConfig.getInstance().getI18nModules().add("i18n/isp/auth_error");
}
/**
* 开启文档本地微服务文档地址http://localhost:2222/doc.html
* http://ip:port/v2/api-docs
*/
@Configuration
@EnableSwagger2
public static class Swagger2 extends SwaggerSupport {
@Override
protected String getDocTitle() {
return "开放平台API";
}
@Override
protected boolean swaggerAccessProtected() {
return false;
}
}
}

@ -0,0 +1,43 @@
package com.gitee.sop.openapi.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(@Autowired RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
redisTemplate.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
redisTemplate.setHashKeySerializer(stringRedisSerializer);
// value采用jackson序列化方式
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value采用jackson序列化方式
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}

@ -0,0 +1,80 @@
package com.gitee.sop.openapi.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/***
*
* @author wangping created on 2020/11/2 17:22
*/
@Slf4j
@Component
public class RedisOperator {
private final RedisTemplate<String, Object> redisTemplate;
public RedisOperator(final RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
public boolean expire(String key, long time) {
try {
if (time >= 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
log.error("redisTemplate.expire.Error", e);
return false;
}
}
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
log.error("redisTemplate.set.Error", e);
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key
* @param value
* @param time 时间() time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
log.error("redisTemplate.setAndExpire.Error", e);
return false;
}
}
public boolean del(String key) {
return redisTemplate.delete(key);
}
}

@ -0,0 +1,68 @@
package com.gitee.sop.openapi.controller;
import com.gitee.sop.openapi.config.RedisOperator;
import com.gitee.sop.openapi.po.AuthCodePo;
import com.gitee.sop.openapi.vo.AuthCodeVo;
import com.gitee.sop.openapi.service.AuthCodeService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotBlank;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
@Api(tags = "超浏授权接口")
public class AuthCodeController {
private final AuthCodeService authCodeService;
private final RedisOperator redisOperator;
@Value("${test.redis}")
private String testRedis;
public void setTestRedis(String testRedis) {
this.testRedis = testRedis;
}
public String getTestRedis() {
return testRedis;
}
public AuthCodeController(final AuthCodeService authCodeService, final RedisOperator redisOperator) {
this.authCodeService = authCodeService;
this.redisOperator = redisOperator;
}
@ApiOperation(value = "获取code")
@PostMapping("/auth/code")
public AuthCodeVo getCode(@RequestBody AuthCodePo authCodePo) {
return authCodeService.getCode(authCodePo);
}
@ApiOperation(value = "解除授权")
@PostMapping("/auth/cancel")
public void cancelToken(@NotBlank(message = "{isp.auth.user_id_empty_error}") Long userId) {
authCodeService.cancelToken(userId);
}
@ApiOperation(value = "测试接口")
@GetMapping("/test")
public Map<String, Object> test() {
Map<String, Object> map = new HashMap<>();
map.put("code", 0);
map.put("msg", "success");
redisOperator.set("test11", testRedis);
return map;
}
}

@ -0,0 +1,27 @@
package com.gitee.sop.openapi.controller;
import com.gitee.sop.openapi.po.AuthTokePo;
import com.gitee.sop.servercommon.annotation.Open;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@Api(tags = "插件应用获取token接口")
public class AuthTokenController {
@Open(value = "auth.get.token", ignoreValidate = true, mergeResult = false, needToken = false)
@PostMapping(value = "/token")
@ApiOperation(value = "code换token", notes = "插件服务器获取token")
public String getToken(@RequestBody AuthTokePo tokePo) {
return "";
}
}

@ -0,0 +1,61 @@
package com.gitee.sop.openapi.entity;
import com.google.common.base.MoreObjects;
import lombok.Data;
import javax.persistence.*;
import java.util.Date;
/***
*
* @author wangping created on 2020/11/4 18:59
*/
@Data
@MappedSuperclass
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "gmt_create", nullable = false)
private Date gmtCreate;
@Column(name = "gmt_modified")
private Date gmtModified;
@PrePersist
protected void prePersist() {
if (this.gmtCreate == null) {
gmtCreate = new Date();
}
if (this.gmtModified == null) {
gmtModified = new Date();
}
}
@PreUpdate
protected void preUpdate() {
this.gmtModified = new Date();
}
@PreRemove
protected void preRemove() {
this.gmtModified = new Date();
}
protected MoreObjects.ToStringHelper toStringHelper() {
return MoreObjects.toStringHelper(this)
.omitNullValues()
.add("id", id)
.add("gmtCreate", gmtCreate)
.add("gmtModified", gmtModified);
}
@Override
public String toString() {
return toStringHelper().toString();
}
}

@ -0,0 +1,42 @@
package com.gitee.sop.openapi.entity;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
/***
*
* @author wangping created on 2020/11/2 16:57
*/
@Data
@Entity
@Table(name = "isv_auth_token")
public class IsvAuthTokenEntity extends BaseEntity {
/** appKey, 数据库字段:app_key */
@Column(name = "app_key")
private String appKey;
/** userId, 数据库字段:user_id */
@Column(name = "user_id")
private Long userId;
/** 1启用,2禁用, 数据库字段:status */
@Column(name = "status", columnDefinition = "Bit default '1'")
private Boolean status = true;
/** appAuthCode 数据库字段:app_auth_code */
@Column(name = "app_auth_code")
private String appAuthCode;
/** appAuthToken 数据库字段:app_auth_token */
@Column(name = "app_auth_token")
private String appAuthToken;
/** appRefreshToken 数据库字段:app_refresh_token */
@Column(name = "app_refresh_token")
private String appRefreshToken;
}

@ -0,0 +1,33 @@
package com.gitee.sop.openapi.entity;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
/***
*
* @author wangping created on 2020/11/2 16:22
*/
@Data
@Entity
@Table(name = "isv_info")
public class IsvInfoEntity extends BaseEntity {
/** appKey, 数据库字段:app_key */
@Column(name = "app_key")
private String appKey;
/** 1启用,2禁用, 数据库字段:status */
@Column(name = "status")
private Byte status;
/** 1:RSA2,2:MD5 */
@Column(name = "sign_type")
private Byte signType;
@Column(name = "remark")
private String remark;
}

@ -0,0 +1,26 @@
package com.gitee.sop.openapi.po;
import com.gitee.sop.servercommon.param.validation.Group1;
import com.gitee.sop.servercommon.param.validation.Group2;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Data
public class AuthCodePo {
@ApiModelProperty(value = "应用ID", required = true, example = "test1", position = 1)
@NotBlank(message = "{isp.auth.app_id_empty_error}", groups = Group1.class)
@Length(min = 1, max = 64, message = "{isp.auth.app_id_length_error}=1,64", groups = Group2.class)
private String appId;
@ApiModelProperty(value = "用户ID", required = true, example = "1", position = 2)
@NotNull(message = "{isp.auth.user_id_empty_error}")
private Long userId;
}

@ -0,0 +1,45 @@
package com.gitee.sop.openapi.po;
import com.gitee.sop.servercommon.param.validation.Group1;
import com.gitee.sop.servercommon.param.validation.Group2;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
/***
*
* @author wangping created on 2020/11/2 18:31
*/
@Data
public class AuthTokePo {
/**
* 授权码.与refresh_token二选一用户对应用授权后得到即第一步中开发者获取到的app_auth_code值
*/
@ApiModelProperty(value = "应用ID", example = "test1", position = 1)
@NotBlank(message = "{isp.auth.app_id_empty_error}", groups = Group1.class)
@Length(min = 1, max = 64, message = "{isp.auth.app_id_length_error}=1,64", groups = Group2.class)
private String code;
/**
* 授权类型<br>
* 如果使用app_auth_code换取token则为authorization_code
* 如果使用refresh_token换取新的token则为refresh_token
*/
@NotBlank(message = "{isp.auth.user_token_empty_error}", groups = Group1.class)
@Length(min = 1, max = 64, message = "{isp.auth.user_token_length_error}=1,64", groups = Group2.class)
@ApiModelProperty(value = "授权类型不能为空", required = true, example = "1111", position = 2)
private String grantType;
/**
* 刷新令牌.与code二选一可为空刷新令牌时使用
*/
private String refreshToken;
}

@ -0,0 +1,17 @@
package com.gitee.sop.openapi.repository;
import com.gitee.sop.openapi.entity.IsvAuthTokenEntity;
import org.springframework.data.repository.PagingAndSortingRepository;
/***
*
* @author wangping created on 2020/11/4 19:12
*/
public interface IsvAuthTokenRepository extends PagingAndSortingRepository<IsvAuthTokenEntity, Long> {
IsvAuthTokenEntity findFirstByAppKeyAndUserIdAndStatusTrue(String appKey, Long userId);
IsvAuthTokenEntity findFirstByUserIdAndStatusTrue(Long userId);
IsvAuthTokenEntity findFirstByAppAuthCode(String appAuthCode);
}

@ -0,0 +1,14 @@
package com.gitee.sop.openapi.repository;
import com.gitee.sop.openapi.entity.IsvInfoEntity;
import org.springframework.data.repository.PagingAndSortingRepository;
/***
*
* @author wangping created on 2020/11/4 19:11
*/
public interface IsvInfoRepository extends PagingAndSortingRepository<IsvInfoEntity, Long> {
IsvInfoEntity findFirstByAppKey(String appKey);
}

@ -0,0 +1,106 @@
package com.gitee.sop.openapi.service;
import com.alibaba.fastjson.JSONObject;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.sop.openapi.config.RedisOperator;
import com.gitee.sop.openapi.entity.IsvAuthTokenEntity;
import com.gitee.sop.openapi.entity.IsvInfoEntity;
import com.gitee.sop.openapi.po.AuthCodePo;
import com.gitee.sop.openapi.repository.IsvAuthTokenRepository;
import com.gitee.sop.openapi.repository.IsvInfoRepository;
import com.gitee.sop.openapi.vo.AuthCodeVo;
import com.gitee.sop.openapi.util.MD5GeneratorUtil;
import com.gitee.sop.openapi.util.RedisKeyUtil;
import com.gitee.sop.servercommon.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/***
*
* @author wangping created on 2020/11/2 16:28
*/
@Slf4j
@Service
public class AuthCodeService implements AuthService {
private final IsvInfoRepository isvInfoRepository;
private final IsvAuthTokenRepository isvAuthTokenRepository;
private final RedisOperator redisOperator;
public AuthCodeService(final IsvInfoRepository isvInfoRepository,
final IsvAuthTokenRepository isvAuthTokenRepository,
final RedisOperator redisOperator) {
this.isvInfoRepository = isvInfoRepository;
this.isvAuthTokenRepository = isvAuthTokenRepository;
this.redisOperator = redisOperator;
}
@Transactional
public AuthCodeVo getCode(AuthCodePo authCodePo) {
IsvInfoEntity isvInfo = isvInfoRepository.findFirstByAppKey(authCodePo.getAppId());
if (isvInfo == null || isvInfo.getStatus().intValue() == FORBIDDEN) {
log.error("appId已禁用:{}", authCodePo.getAppId());
throw new ServiceException("应用ID未申请或已被禁用");
}
String authCode = MD5GeneratorUtil.generateValue();
IsvAuthTokenEntity authTokenEntity = isvAuthTokenRepository.findFirstByAppKeyAndUserIdAndStatusTrue(authCodePo.getAppId(), authCodePo.getUserId());
AuthCodeVo authCodeVo = new AuthCodeVo();
authCodeVo.setCode(authCode);
if (authTokenEntity == null) {
authCodeVo.setState(UNAUTHORIZED);
authTokenEntity = new IsvAuthTokenEntity();
authTokenEntity.setAppAuthCode(authCode);
authTokenEntity.setAppKey(authCodePo.getAppId());
authTokenEntity.setUserId(authCodePo.getUserId());
}
authTokenEntity.setAppAuthCode(authCode);
isvAuthTokenRepository.save(authTokenEntity);
// 写入redis缓存
try {
String codeKey = RedisKeyUtil.getCodeKey(authCode);
redisOperator.set(codeKey, JSONObject.toJSON(authCodePo), CODE_TIMEOUT);
} catch (Exception e) {
throw new ServiceException("获取code失败");
}
return authCodeVo;
}
@Transactional
public void cancelToken(Long userId) {
// todo check userToken and get userId
Query query = new Query()
.eq("user_id", userId)
.eq("status", ENABLE_TOKEN);
IsvAuthTokenEntity authTokenEntity = isvAuthTokenRepository.findFirstByUserIdAndStatusTrue(userId);
if (authTokenEntity == null) {
throw new ServiceException("用户未授权插件应用");
}
try {
authTokenEntity.setStatus(true);
isvAuthTokenRepository.save(authTokenEntity);
} catch (Exception e) {
throw new ServiceException("解除授权失败,系统忙,请稍后!");
}
String tokenKey = RedisKeyUtil.getAuthTokenKey(authTokenEntity.getAppAuthToken());
String refreshKey = RedisKeyUtil.getRefreshTokenKey(authTokenEntity.getAppRefreshToken());
try {
redisOperator.del(tokenKey);
redisOperator.del(refreshKey);
} catch (Exception e) {
throw new ServiceException("解除授权失败,系统忙,请稍后!");
}
}
}

@ -0,0 +1,20 @@
package com.gitee.sop.openapi.service;
/***
*
* @author wangping created on 2020/11/4 15:41
*/
public interface AuthService {
int FORBIDDEN = 2;
/** 0 插件应用未授权 **/
int UNAUTHORIZED = 0;
/** 1 插件应用已授权 **/
int AUTHORIZED = 1;
Byte ENABLE_TOKEN = 1;
Byte DISABLE_TOKEN = 0;
Long CODE_TIMEOUT = 3600L;
}

@ -0,0 +1,83 @@
package com.gitee.sop.openapi.service;
import com.alibaba.fastjson.JSONObject;
import com.gitee.sop.openapi.config.RedisOperator;
import com.gitee.sop.openapi.entity.IsvAuthTokenEntity;
import com.gitee.sop.openapi.po.AuthCodePo;
import com.gitee.sop.openapi.po.AuthTokePo;
import com.gitee.sop.openapi.repository.IsvAuthTokenRepository;
import com.gitee.sop.openapi.util.GrantType;
import com.gitee.sop.openapi.util.MD5GeneratorUtil;
import com.gitee.sop.openapi.util.RedisKeyUtil;
import com.gitee.sop.openapi.vo.AuthTokenVo;
import com.gitee.sop.servercommon.exception.ServiceException;
import org.springframework.stereotype.Service;
/***
*
* @author wangping created on 2020/11/4 15:21
*/
@Service
public class AuthTokenService implements AuthService {
private final RedisOperator redisOperator;
private final IsvAuthTokenRepository isvAuthTokenRepository;
public AuthTokenService(final RedisOperator redisOperator,
final IsvAuthTokenRepository isvAuthTokenRepository) {
this.redisOperator = redisOperator;
this.isvAuthTokenRepository = isvAuthTokenRepository;
}
public AuthTokenVo getToken(AuthTokePo authTokePo) {
AuthTokenVo tokenVo = new AuthTokenVo();
String grantType = authTokePo.getGrantType();
if (GrantType.AUTHORIZATION_CODE.name().equals(grantType)) {
String codeKey = RedisKeyUtil.getCodeKey(authTokePo.getCode());
Object jsonObj = redisOperator.get(codeKey);
if (jsonObj == null) {
throw new ServiceException("Invalid request");
}
AuthCodePo codePo = JSONObject.parseObject(jsonObj.toString(), AuthCodePo.class);
if (codePo == null) {
throw new ServiceException("Can not found user by code");
}
IsvAuthTokenEntity authTokenEntity = isvAuthTokenRepository.findFirstByAppAuthCode(authTokePo.getCode());
if (authTokenEntity == null) {
throw new ServiceException("Can not found user by code");
}
String authToken = MD5GeneratorUtil.generateValue();
String refreshToken = MD5GeneratorUtil.generateValue();
authTokenEntity.setStatus(true);
authTokenEntity.setAppAuthToken(authToken);
authTokenEntity.setAppRefreshToken(refreshToken);
isvAuthTokenRepository.save(authTokenEntity);
String authTokenKey = RedisKeyUtil.getAuthTokenKey(authToken);
String refreshTokenKey = RedisKeyUtil.getRefreshTokenKey(refreshToken);
redisOperator.set(authTokenKey, codePo);
redisOperator.set(refreshTokenKey, codePo);
tokenVo.setAppAuthToken(authToken);
tokenVo.setAppRefreshToken(refreshToken);
tokenVo.setUserId(codePo.getUserId());
return tokenVo;
} else if (GrantType.REFRESH_TOKEN.name().equals(grantType)) {
return tokenVo;
} else {
throw new ServiceException("Invalid grant");
}
}
}

@ -0,0 +1,10 @@
package com.gitee.sop.openapi.util;
/***
*
* @author wangping created on 2020/11/2 16:42
*/
public enum AuthErrorEnum {
}

@ -0,0 +1,23 @@
package com.gitee.sop.openapi.util;
/***
*
* @author wangping created on 2020/11/4 15:25
*/
public enum GrantType {
AUTHORIZATION_CODE("authorization_code"),
PASSWORD("password"),
REFRESH_TOKEN("refresh_token"),
CLIENT_CREDENTIALS("client_credentials");
private String grantType;
private GrantType(String grantType) {
this.grantType = grantType;
}
public String toString() {
return this.grantType;
}
}

@ -0,0 +1,27 @@
package com.gitee.sop.openapi.util;
import org.slf4j.helpers.FormattingTuple;
import org.slf4j.helpers.MessageFormatter;
/***
*
* @author wangping created on 2020/11/2 18:14
*/
public class LogFormatUtil {
/**
* append log with pattern
*
* @param appendLogPattern like "aaa {} bbb {} ccc"
* @param appendLogArguments like "111, true"
*/
public static String log(String appendLogPattern, Object ... appendLogArguments) {
FormattingTuple ft = MessageFormatter.arrayFormat(appendLogPattern, appendLogArguments);
return ft.getMessage();
}
public static void main(String[] args) {
System.out.println(log("My name is {}, from {}", "hanmeimei", "London"));
}
}

@ -0,0 +1,53 @@
package com.gitee.sop.openapi.util;
import com.gitee.sop.servercommon.exception.ServiceException;
import java.security.MessageDigest;
import java.util.UUID;
/***
*
* @author wangping created on 2020/11/2 16:47
*/
public class MD5GeneratorUtil {
private static final char[] hexCode = "0123456789abcdef".toCharArray();
private MD5GeneratorUtil() {
}
public static String generateValue() throws ServiceException {
return generateValue(UUID.randomUUID().toString());
}
public static String toHexString(byte[] data) {
if (data == null) {
return null;
} else {
StringBuilder r = new StringBuilder(data.length * 2);
byte[] arr$ = data;
int len$ = data.length;
for (int i$ = 0; i$ < len$; ++i$) {
byte b = arr$[i$];
r.append(hexCode[b >> 4 & 15]);
r.append(hexCode[b & 15]);
}
return r.toString();
}
}
public static String generateValue(String param) throws ServiceException {
try {
MessageDigest algorithm = MessageDigest.getInstance("MD5");
algorithm.reset();
algorithm.update(param.getBytes());
byte[] messageDigest = algorithm.digest();
return toHexString(messageDigest);
} catch (Exception var4) {
throw new ServiceException("OAuth Token cannot be generated.");
}
}
}

@ -0,0 +1,30 @@
package com.gitee.sop.openapi.util;
import com.google.common.base.Joiner;
/***
*
* @author wangping created on 2020/11/2 17:47
*/
public class RedisKeyUtil {
public static String CODE_PREFIX = "sop:app_auth_code";
public static String AUTH_TOKEN_PREFIX = "sop:app_auth_token";
public static String REFRESH_TOKEN_PREFIX = "sop:app_refresh_token";
private static final Joiner JOINER = Joiner.on(":");
public static String getCodeKey(String code) {
return JOINER.join(CODE_PREFIX, code);
}
public static String getAuthTokenKey(String token) {
return JOINER.join(AUTH_TOKEN_PREFIX, token);
}
public static String getRefreshTokenKey(String refreshToken) {
return JOINER.join(REFRESH_TOKEN_PREFIX, refreshToken);
}
}

@ -0,0 +1,9 @@
package com.gitee.sop.openapi.vo;
import lombok.Data;
@Data
public class AuthCodeVo {
private String code;
private Integer state;
}

@ -0,0 +1,40 @@
package com.gitee.sop.openapi.vo;
import lombok.Data;
/***
*
* @author wangping created on 2020/11/4 15:22
*/
@Data
public class AuthTokenVo {
/**
* 授权令牌
*/
private String appAuthToken;
/**
* 用户id
*/
private Long userId;
/**
* 令牌有效期. -1:永久有效
*/
private long expiresIn = -1;
/**
* 刷新令牌有效期. -1:永久有效
*/
private long reExpiresIn = -1;
/**
* 刷新令牌时使用
* 刷新令牌后我们会保证老的app_auth_token从刷新开始10分钟内可继续使用请及时替换为最新token
*/
private String appRefreshToken;
}

@ -0,0 +1,2 @@
app.id=open-apis
jdkVersion=1.8

@ -0,0 +1,16 @@
spring:
redis:
cluster:
nodes:
- 10.1.11.46:7000
- 10.1.11.46:7001
- 10.1.11.46:7002
jedis:
pool:
max-active: 10
max-wait: -1
max-idle: 3
min-idle: 1
timeout: 60000
password:

@ -0,0 +1,16 @@
spring:
redis:
sentinel:
master: mymaster
nodes:
- 192.168.0.114:26379
- 192.168.0.114:26380
- 192.168.0.114:26381
jedis:
pool:
max-active: 10
max-wait: -1
max-idle: 9
min-idle: 0
timeout: 60000
password:

@ -0,0 +1,13 @@
spring:
redis:
host: 52.81.162.154
port: 8095
jedis:
pool:
max-active: 10
max-wait: -1
max-idle: 9
min-idle: 1
database: 0
timeout: 60000
password: zixun@2020!

@ -0,0 +1,37 @@
spring.profiles.active=standalone
server.port=8089
spring.application.name=open-apis
# 注册中心
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
## 数据库配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/sop?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.hikari.connectionInitSql=set names utf8mb4
#连接池
spring.datasource.hikari.pool-name=HikariCP
spring.datasource.hikari.max-lifetime=500000
spring.datasource.hikari.minimum-idle=2
spring.datasource.hikari.idle-timeout=60000
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.connection-test-query=select 1
# Naming strategy
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.hibernate.globally_quoted_identifiers=true
spring.jpa.properties.hibernate.globally_quoted_identifiers=true
spring.jpa.show-sql=false
# Apollo
app.id=open-apis
apollo.bootstrap.enabled=true
apollo.bootstrap.namespaces=application
apollo.bootstrap.eagerLoad.enable=true
test.redis=wangping

@ -0,0 +1,4 @@
isp.auth.user_id_empty_error=the user_id can NOT be null
isp.auth.app_id_empty_error=the app_id can NOT be null
isp.auth.app_id_length_error=The app_id length must >= {0} and <= {1}

@ -0,0 +1,9 @@
# 用户登录凭证不能为空
isp.auth.user_id_empty_error=\u7528\u6237\u0049\u0044\u4e0d\u80fd\u4e3a\u7a7a
# 用户登录凭证长度必须在{0}和{1}之间
#isp.auth.user_token_length_error=\u7528\u6237\u767b\u5f55\u51ed\u8bc1\u957f\u5ea6\u5fc5\u987b\u5728{0}\u548c{1}\u4e4b\u95f4
# 应用ID不能为空
isp.auth.app_id_empty_error=\u5e94\u7528\u0049\u0044\u4e0d\u80fd\u4e3a\u7a7a
# 应用ID长度必须在{0}和{1}之间
isp.auth.app_id_length_error=\u5e94\u7528\u0049\u0044\u957f\u5ea6\u5fc5\u987b\u5728{0}\u548c{1}\u4e4b\u95f4

@ -0,0 +1,5 @@
# 错误配置
# 系统配置
isp.error_isv.common-error=The system is busy.
isp.error_isv.invalid-parameter=Invalid parameter, {0}

@ -0,0 +1,9 @@
# 错误配置
# 系统繁忙
isp.error_isv.common-error=\u7cfb\u7edf\u7e41\u5fd9
# 参数无效
isp.error_isv.invalid-parameter=\u53c2\u6570\u65e0\u6548, {0}
# ==== 参数配置 ====

@ -0,0 +1,20 @@
package com.gitee.sop;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
/**
* Unit test for simple App.
*/
public class AppTest
{
/**
* Rigorous Test :-)
*/
@Test
public void shouldAnswerWithTrue()
{
assertTrue( true );
}
}
Loading…
Cancel
Save