forked from rongchao/epmet-cloud-rizhao
32 changed files with 1056 additions and 21 deletions
@ -0,0 +1,15 @@ |
|||
<?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"> |
|||
<parent> |
|||
<artifactId>epmet-commons</artifactId> |
|||
<groupId>com.epmet</groupId> |
|||
<version>2.0.0</version> |
|||
</parent> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
|
|||
<artifactId>epmet-commons-openapi</artifactId> |
|||
|
|||
|
|||
</project> |
@ -0,0 +1,8 @@ |
|||
package com.epmet.openapi.constant; |
|||
|
|||
/** |
|||
* 认证方式 |
|||
*/ |
|||
public interface AuthTypes { |
|||
String TAKE_TOKEN = "take_token"; |
|||
} |
@ -0,0 +1,10 @@ |
|||
package com.epmet.openapi.constant; |
|||
|
|||
/** |
|||
* 集群内的Header key |
|||
*/ |
|||
public interface InClusterHeaderKeys { |
|||
|
|||
String APP_ID = "AppId"; |
|||
|
|||
} |
@ -0,0 +1,14 @@ |
|||
package com.epmet.openapi.constant; |
|||
|
|||
/** |
|||
* url请求参数key |
|||
*/ |
|||
public class RequestParamKeys { |
|||
|
|||
public static String APP_ID = "app_id"; |
|||
public static String AUTH_TYPE = "auth_type"; |
|||
public static String TIMESTAMP = "timestamp"; |
|||
public static String SIGN = "sign"; |
|||
public static String NONCE = "nonce"; |
|||
|
|||
} |
@ -0,0 +1,26 @@ |
|||
<?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"> |
|||
<parent> |
|||
<artifactId>epmet-commons</artifactId> |
|||
<groupId>com.epmet</groupId> |
|||
<version>2.0.0</version> |
|||
</parent> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
|
|||
<artifactId>epmet-commons-security</artifactId> |
|||
|
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>com.epmet</groupId> |
|||
<artifactId>epmet-commons-tools</artifactId> |
|||
<version>2.0.0</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>io.jsonwebtoken</groupId> |
|||
<artifactId>jjwt</artifactId> |
|||
<version>0.7.0</version> |
|||
</dependency> |
|||
</dependencies> |
|||
</project> |
@ -0,0 +1,131 @@ |
|||
/** |
|||
* Copyright (c) 2018 人人开源 All rights reserved. |
|||
* <p> |
|||
* https://www.renren.io
|
|||
* <p> |
|||
* 版权所有,侵权必究! |
|||
*/ |
|||
|
|||
package com.epmet.commons.security.jwt; |
|||
|
|||
import io.jsonwebtoken.Claims; |
|||
import io.jsonwebtoken.Jwts; |
|||
import io.jsonwebtoken.SignatureAlgorithm; |
|||
import org.joda.time.DateTime; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.util.Date; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* Jwt工具类 |
|||
* |
|||
* @author Mark sunlightcs@gmail.com |
|||
* @since 1.0.0 |
|||
*/ |
|||
@Component |
|||
public class JwtUtils { |
|||
private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class); |
|||
|
|||
public Claims getClaimByToken(String token, String secret) { |
|||
try { |
|||
return Jwts.parser() |
|||
.setSigningKey(secret) |
|||
.parseClaimsJws(token) |
|||
.getBody(); |
|||
} catch (Exception e) { |
|||
logger.debug("validate is token error, token = " + token, e); |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* @return java.util.Date |
|||
* @param token |
|||
* @Author yinzuomei |
|||
* @Description 获取token的有效期截止时间 |
|||
* @Date 2020/3/18 22:17 |
|||
**/ |
|||
public Date getExpiration(String token, String secret){ |
|||
try { |
|||
return Jwts.parser() |
|||
.setSigningKey(secret) |
|||
.parseClaimsJws(token) |
|||
.getBody().getExpiration(); |
|||
} catch (Exception e) { |
|||
logger.debug("validate is token error, token = " + token, e); |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* @return java.lang.String |
|||
* @Author yinzuomei |
|||
* @Description 根据app+client+userId生成token |
|||
* @Date 2020/3/18 22:29 |
|||
**/ |
|||
public String createToken(Map<String, Object> claims, int expire, String secret) { |
|||
return Jwts.builder() |
|||
.setHeaderParam("typ", "JWT") |
|||
.setClaims(claims) |
|||
.setIssuedAt(new Date()) |
|||
.setExpiration(DateTime.now().plusSeconds(expire).toDate()) |
|||
.signWith(SignatureAlgorithm.HS512, secret) |
|||
.compact(); |
|||
} |
|||
|
|||
/** |
|||
* @Description 创建Token |
|||
* @return Token |
|||
* @param claims 载荷信息 |
|||
* @param expireTime 过期时间 |
|||
* @param secret 秘钥 |
|||
* @author wxz |
|||
* @date 2021.03.26 13:33 |
|||
*/ |
|||
public String createToken(Map<String, Object> claims, Date expireTime, String secret) { |
|||
return Jwts.builder() |
|||
.setHeaderParam("typ", "JWT") |
|||
.setClaims(claims) |
|||
.setIssuedAt(new Date()) |
|||
.setExpiration(expireTime) |
|||
.signWith(SignatureAlgorithm.HS512, secret) |
|||
.compact(); |
|||
} |
|||
|
|||
/** |
|||
* token是否过期 |
|||
* |
|||
* @return true:过期 |
|||
*/ |
|||
public boolean isTokenExpired(Date expiration) { |
|||
return expiration.before(new Date()); |
|||
} |
|||
|
|||
public static void main(String[] args) { |
|||
Map<String, Object> map=new HashMap<>(); |
|||
map.put("app","gov"); |
|||
map.put("client","wxmp"); |
|||
map.put("userId","100526ABC"); |
|||
String tokenStr=Jwts.builder() |
|||
.setHeaderParam("typ", "JWT") |
|||
.setClaims(map) |
|||
.setIssuedAt(new Date()) |
|||
.setExpiration(DateTime.now().plusSeconds(604800).toDate()) |
|||
.signWith(SignatureAlgorithm.HS512, "7016867071f0ebf1c46f123eaaf4b9d6[elink.epmet]") |
|||
.compact(); |
|||
System.out.println(tokenStr); |
|||
Claims claims= Jwts.parser() |
|||
.setSigningKey("7016867071f0ebf1c46f123eaaf4b9d6[elink.epmet]") |
|||
.parseClaimsJws(tokenStr) |
|||
.getBody(); |
|||
System.out.println("app="+ claims.get("app")); |
|||
System.out.println("client="+ claims.get("client")); |
|||
System.out.println("userId="+ claims.get("userId")); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,109 @@ |
|||
package com.epmet.commons.security.sign.openapi; |
|||
|
|||
import com.epmet.commons.tools.utils.Md5Util; |
|||
|
|||
import java.util.*; |
|||
|
|||
/** |
|||
* OpenApi签名工具 |
|||
*/ |
|||
public class OpenApiSignUtils { |
|||
|
|||
/** |
|||
* @Description 创建签名 |
|||
* @return |
|||
* @author wxz |
|||
* @date 2021.03.22 16:47 |
|||
*/ |
|||
public static String createSign(Map<String, String> contentMap, String signKey) { |
|||
String str2beSigned = mapToSignStr(contentMap); |
|||
str2beSigned = str2beSigned.concat("&sign_key=").concat(signKey); |
|||
return Md5Util.md5(str2beSigned); |
|||
} |
|||
|
|||
/** |
|||
* @Description 验签 |
|||
* @return |
|||
* @author wxz |
|||
* @date 2021.03.22 16:51 |
|||
*/ |
|||
public static boolean checkSign(Map<String, String> contentMap, String signKey) { |
|||
String newSign = createSign(contentMap, signKey); |
|||
return newSign.equals(contentMap.get("sign")); |
|||
} |
|||
|
|||
/** |
|||
* @Description map转化为签名明文 |
|||
* @return |
|||
* @author wxz |
|||
* @date 2021.03.22 16:47 |
|||
*/ |
|||
public static String mapToSignStr(Map<String, String> map) { |
|||
Set<String> keySet = map.keySet(); |
|||
String[] keyArray = (String[])keySet.toArray(new String[keySet.size()]); |
|||
Arrays.sort(keyArray); |
|||
StringBuilder sb = new StringBuilder(); |
|||
|
|||
for(int i = 0; i < keyArray.length; ++i) { |
|||
String key = keyArray[i]; |
|||
String val = (String)map.get(key); |
|||
if (val != null && val.trim().length() > 0 && !"sign".equals(key)) { |
|||
if (!sb.toString().isEmpty()) { |
|||
sb.append("&"); |
|||
} |
|||
|
|||
sb.append(key).append("=").append((String)map.get(key)); |
|||
} |
|||
} |
|||
|
|||
return sb.toString(); |
|||
} |
|||
|
|||
public static void main(String[] args) { |
|||
generateGetAccessTokenSign(); |
|||
System.out.println("=============="); |
|||
generateGetOrgDetailSign(); |
|||
} |
|||
|
|||
private static void generateGetAccessTokenSign() { |
|||
long now = System.currentTimeMillis(); |
|||
System.out.println(now); |
|||
|
|||
String uuid = UUID.randomUUID().toString().replace("-", ""); |
|||
|
|||
HashMap<String, String> content = new HashMap<>(); |
|||
content.put("app_id", "7d98b8af2d05752b4225709c4cfd4bd0"); |
|||
content.put("timestamp", String.valueOf(now)); |
|||
content.put("nonce", uuid); |
|||
content.put("auth_type", "take_token"); |
|||
|
|||
String secret = "3209ee9f41704482be1a1fb5873a25376f2899191ca846119d44168316bc3e44"; |
|||
|
|||
String sign = createSign(content, secret); |
|||
|
|||
System.out.println("时间戳:" + now); |
|||
System.out.println("随机数:" + uuid); |
|||
System.out.println("签名:" + sign); |
|||
} |
|||
|
|||
private static void generateGetOrgDetailSign() { |
|||
long now = System.currentTimeMillis(); |
|||
String uuid = UUID.randomUUID().toString().replace("-", "");; |
|||
System.out.println("时间戳:" + now); |
|||
System.out.println("随机数:" + uuid); |
|||
|
|||
HashMap<String, String> content = new HashMap<>(); |
|||
//content.put("orgId", "aaa");
|
|||
//content.put("test", null);
|
|||
content.put("gridId", "12128e0f614f1c00a058ea9a107033b2"); |
|||
content.put("app_id", "7d98b8af2d05752b4225709c4cfd4bd0"); |
|||
content.put("timestamp", String.valueOf(now)); |
|||
content.put("nonce", uuid); |
|||
content.put("auth_type", "take_token"); |
|||
|
|||
String secret = "3209ee9f41704482be1a1fb5873a25376f2899191ca846119d44168316bc3e44"; |
|||
|
|||
String sign = createSign(content, secret); |
|||
System.out.println("签名:" + sign); |
|||
} |
|||
} |
@ -0,0 +1,91 @@ |
|||
package com.epmet.auth; |
|||
|
|||
import com.epmet.commons.security.jwt.JwtUtils; |
|||
import com.epmet.commons.security.sign.openapi.OpenApiSignUtils; |
|||
import com.epmet.commons.tools.exception.EpmetErrorCode; |
|||
import com.epmet.commons.tools.exception.RenException; |
|||
import com.epmet.commons.tools.redis.RedisKeys; |
|||
import com.epmet.commons.tools.redis.RedisUtils; |
|||
import com.epmet.commons.tools.utils.Result; |
|||
import com.epmet.commons.tools.utils.SpringContextUtils; |
|||
import com.epmet.feign.EpmetCommonServiceOpenFeignClient; |
|||
import com.epmet.openapi.constant.InClusterHeaderKeys; |
|||
import com.epmet.openapi.constant.RequestParamKeys; |
|||
import io.jsonwebtoken.Claims; |
|||
import io.jsonwebtoken.Jwts; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.http.server.reactive.ServerHttpRequest; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.web.server.ServerWebExchange; |
|||
|
|||
import java.util.Date; |
|||
|
|||
/** |
|||
* 外部应用认证处理器:来平台token的方式 |
|||
*/ |
|||
@Component |
|||
public class ExtAppTakeTokenAuthProcessor extends ExtAppAuthProcessor { |
|||
|
|||
@Autowired |
|||
private JwtUtils jwtTokenUtils; |
|||
|
|||
@Autowired |
|||
private RedisUtils redisUtils; |
|||
|
|||
@Override |
|||
public void auth(String appId, String token, Long ts, ServerWebExchange exchange) { |
|||
String secret = getSecret(appId); |
|||
|
|||
// 1.过期验证
|
|||
String accessTokenInCache = redisUtils.getString(RedisKeys.getOpenApiAccessTokenKey(appId)); |
|||
Date expiration = jwtTokenUtils.getExpiration(token, secret); |
|||
if (StringUtils.isBlank(accessTokenInCache) || |
|||
expiration == null || |
|||
jwtTokenUtils.isTokenExpired(expiration) |
|||
) { |
|||
|
|||
throw new RenException(EpmetErrorCode.OPEN_API_TOKEN_EXPIRED.getCode(), |
|||
EpmetErrorCode.OPEN_API_TOKEN_EXPIRED.getMsg()); |
|||
} |
|||
|
|||
// 2.验签
|
|||
// 验签暂时放到具体接口中,不放在gateway
|
|||
//openApiSignUtils.checkSign();
|
|||
|
|||
// 2. 获取claims
|
|||
Claims claims = jwtTokenUtils.getClaimByToken(token, secret); |
|||
String appIdInAccessToken = claims.get(RequestParamKeys.APP_ID, String.class); |
|||
|
|||
if (!appId.equals(appIdInAccessToken)) { |
|||
// 参数列表的appId和token中封装的不一致
|
|||
throw new RenException(EpmetErrorCode.OPEN_API_PARAMS_APPID_DIFF.getCode(), |
|||
EpmetErrorCode.OPEN_API_PARAMS_APPID_DIFF.getMsg()); |
|||
} |
|||
|
|||
// 3.将app_id放入header中
|
|||
ServerHttpRequest.Builder mutate = exchange.getRequest().mutate(); |
|||
mutate.header(InClusterHeaderKeys.APP_ID, new String[]{appId}); |
|||
exchange.mutate().request(mutate.build()).build(); |
|||
} |
|||
|
|||
/** |
|||
* @return |
|||
* @Description 获取秘钥 |
|||
* @author wxz |
|||
* @date 2021.03.23 14:12 |
|||
*/ |
|||
private String getSecret(String appId) { |
|||
EpmetCommonServiceOpenFeignClient commonService = SpringContextUtils.getBean(EpmetCommonServiceOpenFeignClient.class); |
|||
Result<String> result = commonService.getSecret(appId); |
|||
if (result == null || !result.success()) { |
|||
throw new RenException("fetchToken方式的外部应用认证,获取secret失败"); |
|||
} |
|||
String secret = result.getData(); |
|||
if (StringUtils.isBlank(secret)) { |
|||
throw new RenException("fetchToken方式的外部应用认证,获取secret失败"); |
|||
} |
|||
|
|||
return secret; |
|||
} |
|||
} |
@ -0,0 +1,19 @@ |
|||
package com.epmet.utils; |
|||
|
|||
import org.springframework.http.server.reactive.ServerHttpRequest; |
|||
import org.springframework.util.MultiValueMap; |
|||
|
|||
public class ServerHttpRequestUtils { |
|||
|
|||
/** |
|||
* @Description 从url中获取appId |
|||
* @return |
|||
* @author wxz |
|||
* @date 2021.03.25 15:13 |
|||
*/ |
|||
public static String getRequestParam(ServerHttpRequest request, String paramName) { |
|||
MultiValueMap<String, String> queryParams = request.getQueryParams(); |
|||
return queryParams.getFirst(paramName); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,15 @@ |
|||
package com.epmet.dto.form.openapi; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import javax.validation.constraints.NotBlank; |
|||
|
|||
@Data |
|||
public class GetOrgDetailFormDTO extends OpenApiBaseFormDTO { |
|||
|
|||
@NotBlank(message = "orgId不能为空") |
|||
private String orgId; |
|||
|
|||
private String test; |
|||
|
|||
} |
@ -0,0 +1,31 @@ |
|||
package com.epmet.dto.form.openapi; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import javax.validation.constraints.NotBlank; |
|||
import javax.validation.constraints.NotNull; |
|||
|
|||
/** |
|||
* open api基础类 |
|||
*/ |
|||
@Data |
|||
public class OpenApiBaseFormDTO { |
|||
|
|||
public interface GetAccessTokenGroup {} |
|||
|
|||
@NotBlank(message = "签名不能为空", groups = { GetAccessTokenGroup.class }) |
|||
private String sign; |
|||
|
|||
/** |
|||
* 时间戳,ms |
|||
*/ |
|||
@NotNull(message = "时间戳不能为空", groups = { GetAccessTokenGroup.class }) |
|||
private Long timestamp; |
|||
|
|||
/** |
|||
* 随机数,每次请求唯一 |
|||
*/ |
|||
@NotBlank(message = "随机字段nonce不能为空", groups = { GetAccessTokenGroup.class }) |
|||
private String nonce; |
|||
|
|||
} |
@ -0,0 +1,12 @@ |
|||
package com.epmet.dto.result.openapi; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class GetAccessTokenResultDTO { |
|||
|
|||
private String accessToken; |
|||
|
|||
private Long expireTime; |
|||
|
|||
} |
@ -0,0 +1,13 @@ |
|||
package com.epmet.annotation; |
|||
|
|||
import java.lang.annotation.*; |
|||
|
|||
/** |
|||
* OpenApi验签注解 |
|||
*/ |
|||
@Target(ElementType.METHOD) |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Documented |
|||
public @interface OpenApiCheckSign { |
|||
|
|||
} |
@ -0,0 +1,218 @@ |
|||
package com.epmet.aspect; |
|||
|
|||
import com.epmet.commons.mybatis.aspect.DataFilterAspect; |
|||
import com.epmet.commons.security.sign.openapi.OpenApiSignUtils; |
|||
import com.epmet.commons.tools.exception.EpmetErrorCode; |
|||
import com.epmet.commons.tools.exception.RenException; |
|||
import com.epmet.commons.tools.redis.RedisKeys; |
|||
import com.epmet.commons.tools.redis.RedisUtils; |
|||
import com.epmet.commons.tools.utils.ConvertUtils; |
|||
import com.epmet.commons.tools.utils.Result; |
|||
import com.epmet.feign.EpmetCommonServiceOpenFeignClient; |
|||
import com.epmet.openapi.constant.RequestParamKeys; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.aspectj.lang.JoinPoint; |
|||
import org.aspectj.lang.annotation.Aspect; |
|||
import org.aspectj.lang.annotation.Before; |
|||
import org.aspectj.lang.reflect.MethodSignature; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.core.annotation.Order; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.web.bind.annotation.RequestBody; |
|||
import org.springframework.web.context.request.RequestAttributes; |
|||
import org.springframework.web.context.request.RequestContextHolder; |
|||
import org.springframework.web.context.request.ServletRequestAttributes; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
import java.lang.reflect.Method; |
|||
import java.lang.reflect.Parameter; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* OpenApi检查请求切面 |
|||
* 1.验签防止参数篡改 |
|||
* 2.timestamp+nonce防止请求重放攻击 |
|||
*/ |
|||
@Aspect |
|||
@Component |
|||
@Order(1) |
|||
public class OpenApiRequestCheckAspect { |
|||
|
|||
@Autowired |
|||
private RedisUtils redisUtils; |
|||
|
|||
@Autowired |
|||
private EpmetCommonServiceOpenFeignClient commonServiceOpenFeignClient; |
|||
|
|||
//请求时差单位:s
|
|||
long requestTimeSecDiff = 120; |
|||
//请求时差,单位:ms
|
|||
long requestTimeMillSecDiff = requestTimeSecDiff * 1000;//单位:ms
|
|||
|
|||
private static final Logger log = LoggerFactory.getLogger(DataFilterAspect.class); |
|||
|
|||
/** |
|||
* @Description 验签 |
|||
* @return |
|||
* @author wxz |
|||
* @date 2021.03.24 13:39 |
|||
*/ |
|||
@Before("execution(* com.epmet.controller.*Controller*.*(..)) && @annotation(com.epmet.annotation.OpenApiCheckSign)") |
|||
public void check(JoinPoint point) { |
|||
Object[] args = point.getArgs(); |
|||
MethodSignature methodSignature = (MethodSignature) point.getSignature(); |
|||
Method method = methodSignature.getMethod(); |
|||
Parameter[] parameters = method.getParameters(); |
|||
|
|||
HttpServletRequest request = getRequest(); |
|||
|
|||
Map<String, String> argMap = new HashMap<>(); |
|||
for (int i = 0; i < parameters.length; i++) { |
|||
if (parameters[i].isAnnotationPresent(RequestBody.class)) { |
|||
try { |
|||
argMap = ConvertUtils.entityToMap(args[i]); |
|||
} catch (Exception e) { |
|||
throw new RenException("验签参数转化发生异常"); |
|||
} |
|||
|
|||
break; |
|||
} |
|||
} |
|||
|
|||
fillRequestParamsInfoArgMap(argMap, request); |
|||
if (!OpenApiSignUtils.checkSign(argMap, getSecret(argMap.get(RequestParamKeys.APP_ID)))) { |
|||
// 验签失败
|
|||
throw new RenException(EpmetErrorCode.OPEN_API_SIGN_ERROR.getCode()); |
|||
} |
|||
checkRepeatRequest(argMap); |
|||
} |
|||
|
|||
/** |
|||
* @Description 填充url请求参数到map中,用来签名 |
|||
* @return |
|||
* @author wxz |
|||
* @date 2021.03.26 10:13 |
|||
*/ |
|||
private void fillRequestParamsInfoArgMap(Map<String, String> argMap, HttpServletRequest request) { |
|||
fillRequestParamsInfoArgMap(argMap, request, RequestParamKeys.APP_ID); |
|||
fillRequestParamsInfoArgMap(argMap, request, RequestParamKeys.AUTH_TYPE); |
|||
fillRequestParamsInfoArgMap(argMap, request, RequestParamKeys.NONCE); |
|||
fillRequestParamsInfoArgMap(argMap, request, RequestParamKeys.TIMESTAMP); |
|||
fillRequestParamsInfoArgMap(argMap, request, RequestParamKeys.SIGN); |
|||
} |
|||
|
|||
private void fillRequestParamsInfoArgMap(Map<String, String> argMap, HttpServletRequest request, String paramName) { |
|||
String paramValue = request.getParameter(paramName); |
|||
if (StringUtils.isNotBlank(paramName)) { |
|||
argMap.put(paramName, paramValue); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 检查请求重放 |
|||
* @param argMap |
|||
*/ |
|||
void checkRepeatRequest(Map<String, String> argMap) { |
|||
String timestampStr = argMap.get(RequestParamKeys.TIMESTAMP); |
|||
if (StringUtils.isBlank(timestampStr)) { |
|||
throw new RenException(EpmetErrorCode.OPEN_API_PARAMS_MISSING.getCode()); |
|||
} |
|||
long timestamp = Long.valueOf(timestampStr).longValue(); |
|||
long now = System.currentTimeMillis(); |
|||
|
|||
if (Math.abs(now - timestamp) > requestTimeMillSecDiff) { |
|||
// 只允许1分钟之内的请求,允许服务器之间时差为1分钟
|
|||
throw new RenException(EpmetErrorCode.OPEN_API_REQUEST_EXPIRED.getCode(), |
|||
String.format("请求已过期,允许时差为%s s", requestTimeSecDiff)); |
|||
} |
|||
String nonce = argMap.get(RequestParamKeys.NONCE); |
|||
String nonceInCache = redisUtils.getString(RedisKeys.getOpenApiNonceKey(nonce)); |
|||
if (StringUtils.isNotBlank(nonceInCache)) { |
|||
throw new RenException(EpmetErrorCode.OPEN_API_REQUEST_REPEAT.getCode()); |
|||
} |
|||
//将nonce缓存到redis,有效期1分钟
|
|||
redisUtils.set(RedisKeys.getOpenApiNonceKey(nonce), System.currentTimeMillis(), requestTimeSecDiff); |
|||
} |
|||
|
|||
/** |
|||
* @return |
|||
* @Description 取secret |
|||
* @author wxz |
|||
* @date 2021.03.24 12:49 |
|||
*/ |
|||
private String getSecret(String appId) { |
|||
String secret = (String) redisUtils.get(RedisKeys.getExternalAppSecretKey(appId)); |
|||
if (StringUtils.isBlank(secret)) { |
|||
Result<String> result = commonServiceOpenFeignClient.getSecret(appId); |
|||
if (!result.success()) { |
|||
throw new RenException("调用common service查询secret失败"); |
|||
} |
|||
secret = result.getData(); |
|||
if (StringUtils.isBlank(secret)) { |
|||
throw new RenException(String.format("根据appId%s没有找到对应的secret", appId)); |
|||
} |
|||
redisUtils.set(RedisKeys.getExternalAppSecretKey(appId), secret); |
|||
} |
|||
return secret; |
|||
} |
|||
|
|||
/** |
|||
* @return |
|||
* @Description 获取request |
|||
* @author wxz |
|||
* @date 2021.03.24 12:52 |
|||
*/ |
|||
public HttpServletRequest getRequest() { |
|||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); |
|||
ServletRequestAttributes sra = (ServletRequestAttributes) requestAttributes; |
|||
return sra.getRequest(); |
|||
} |
|||
|
|||
/** |
|||
* @return |
|||
* @Description 获取appId |
|||
* @author wxz |
|||
* @date 2021.03.24 12:53 |
|||
*/ |
|||
//public String getAppId(Parameter[] parameters, Object[] args) {
|
|||
// HttpServletRequest request = getRequest();
|
|||
// String appId = request.getHeader("AppId");
|
|||
// if (StringUtils.isBlank(appId)) {
|
|||
// for (int i = 0; i < parameters.length; i++) {
|
|||
// if (parameters[i].isAnnotationPresent(RequestBody.class)) {
|
|||
// Object arg = args[i];
|
|||
// try {
|
|||
// appId = getAppIdFromDTO(arg);
|
|||
// } catch (IllegalAccessException e) {
|
|||
// e.printStackTrace();
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
// if (StringUtils.isBlank(appId)) {
|
|||
// throw new RenException("未携带AppId");
|
|||
// }
|
|||
// return appId;
|
|||
//}
|
|||
|
|||
//private String getAppIdFromDTO(Object dto) throws IllegalAccessException {
|
|||
// Field[] declaredFields = dto.getClass().getDeclaredFields();
|
|||
// for (int i = 0; i < declaredFields.length; i++) {
|
|||
// Field field = declaredFields[i];
|
|||
// String fieldName = field.getName();
|
|||
// if ("appId".equals(fieldName)) {
|
|||
// field.setAccessible(true);
|
|||
// String value = (String) field.get(dto);
|
|||
// return value;
|
|||
// }
|
|||
// }
|
|||
// return null;
|
|||
//}
|
|||
|
|||
public static void main(String[] args) { |
|||
System.out.println(System.currentTimeMillis()); |
|||
} |
|||
} |
@ -0,0 +1,15 @@ |
|||
package com.epmet.config; |
|||
|
|||
import lombok.Data; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
@Component |
|||
@Data |
|||
public class OpenApiConfig { |
|||
|
|||
@Value("${openApi.accessToken.expire}") |
|||
private int accessTokenExpire; |
|||
|
|||
} |
@ -0,0 +1,60 @@ |
|||
package com.epmet.controller; |
|||
|
|||
import com.epmet.annotation.OpenApiCheckSign; |
|||
import com.epmet.commons.tools.exception.RenException; |
|||
import com.epmet.commons.tools.redis.RedisKeys; |
|||
import com.epmet.commons.tools.redis.RedisUtils; |
|||
import com.epmet.commons.tools.utils.Result; |
|||
import com.epmet.dto.result.openapi.GetAccessTokenResultDTO; |
|||
import com.epmet.feign.EpmetCommonServiceOpenFeignClient; |
|||
import com.epmet.service.OpenApiAccessTokenService; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.web.bind.annotation.*; |
|||
|
|||
@RestController |
|||
@RequestMapping("open-api") |
|||
public class OpenApiAccessTokenController { |
|||
|
|||
@Autowired |
|||
private OpenApiAccessTokenService openApiAccessTokenService; |
|||
|
|||
@Autowired |
|||
private EpmetCommonServiceOpenFeignClient commonServiceOpenFeignClient; |
|||
|
|||
@Autowired |
|||
private RedisUtils redisUtils; |
|||
|
|||
private Logger logger = LoggerFactory.getLogger(OpenApiAccessTokenController.class); |
|||
|
|||
/** |
|||
* @Description 获取AccessToken |
|||
* @return |
|||
* @author wxz |
|||
* @date 2021.03.23 09:52 |
|||
*/ |
|||
@OpenApiCheckSign |
|||
@PostMapping("get-access-token") |
|||
public Result<GetAccessTokenResultDTO> getAccessToken(@RequestParam("app_id") String appId) { |
|||
// 1.取secret
|
|||
String secret = (String)redisUtils.get(RedisKeys.getExternalAppSecretKey(appId)); |
|||
if (StringUtils.isBlank(secret)) { |
|||
Result<String> result = commonServiceOpenFeignClient.getSecret(appId); |
|||
if (!result.success()) { |
|||
throw new RenException("调用common service查询secret失败"); |
|||
} |
|||
secret = result.getData(); |
|||
if (StringUtils.isBlank(secret)) { |
|||
throw new RenException(String.format("根据appId%s没有找到对应的secret", appId)); |
|||
} |
|||
redisUtils.set(RedisKeys.getExternalAppSecretKey(appId), secret); |
|||
} |
|||
|
|||
//2.生成token
|
|||
GetAccessTokenResultDTO content = openApiAccessTokenService.getAccessToken(appId, secret); |
|||
return new Result<GetAccessTokenResultDTO>().ok(content); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,27 @@ |
|||
package com.epmet.controller; |
|||
|
|||
import com.epmet.annotation.OpenApiCheckSign; |
|||
import com.epmet.commons.tools.utils.Result; |
|||
import com.epmet.dto.form.openapi.GetOrgDetailFormDTO; |
|||
import org.springframework.web.bind.annotation.*; |
|||
|
|||
@RestController |
|||
@RequestMapping("open-api") |
|||
public class OpenApiOrgController { |
|||
|
|||
/** |
|||
* @Description OpenApiCheckSign是验签注解,OpenApi的接口请加上该注解 |
|||
* @return |
|||
* @author wxz |
|||
* @date 2021.03.24 12:55 |
|||
*/ |
|||
@OpenApiCheckSign |
|||
@PostMapping("/get-org-detail") |
|||
public Result getOrgDetail(@RequestBody GetOrgDetailFormDTO input, |
|||
@RequestHeader("AppId") String appId) { |
|||
return new Result().ok("测试org"); |
|||
} |
|||
|
|||
|
|||
|
|||
} |
@ -0,0 +1,17 @@ |
|||
package com.epmet.service; |
|||
|
|||
import com.epmet.dto.result.openapi.GetAccessTokenResultDTO; |
|||
|
|||
/** |
|||
* access token的service |
|||
*/ |
|||
public interface OpenApiAccessTokenService { |
|||
|
|||
/** |
|||
* @Description 获取AccessToken |
|||
* @return |
|||
* @author wxz |
|||
* @date 2021.03.22 22:57 |
|||
*/ |
|||
GetAccessTokenResultDTO getAccessToken(String appId, String secret); |
|||
} |
@ -0,0 +1,45 @@ |
|||
package com.epmet.service.impl; |
|||
|
|||
import com.epmet.commons.security.jwt.JwtUtils; |
|||
import com.epmet.commons.tools.redis.RedisKeys; |
|||
import com.epmet.commons.tools.redis.RedisUtils; |
|||
import com.epmet.config.OpenApiConfig; |
|||
import com.epmet.dto.result.openapi.GetAccessTokenResultDTO; |
|||
import com.epmet.openapi.constant.RequestParamKeys; |
|||
import com.epmet.service.OpenApiAccessTokenService; |
|||
import org.joda.time.DateTime; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import java.util.Date; |
|||
import java.util.HashMap; |
|||
|
|||
@Service |
|||
public class OpenApiAccessTokenServiceImpl implements OpenApiAccessTokenService { |
|||
|
|||
@Autowired |
|||
private JwtUtils jwtTokenUtils; |
|||
|
|||
@Autowired |
|||
private OpenApiConfig openApiConfig; |
|||
|
|||
@Autowired |
|||
private RedisUtils redisUtils; |
|||
|
|||
@Override |
|||
public GetAccessTokenResultDTO getAccessToken(String appId, String secret) { |
|||
HashMap<String, Object> claim = new HashMap<>(); |
|||
claim.put(RequestParamKeys.APP_ID, appId); |
|||
|
|||
Date expireTime = DateTime.now().plusSeconds(openApiConfig.getAccessTokenExpire()).toDate(); |
|||
String token = jwtTokenUtils.createToken(claim, expireTime, secret); |
|||
// 缓存token
|
|||
redisUtils.set(RedisKeys.getOpenApiAccessTokenKey(appId), token, openApiConfig.getAccessTokenExpire()); |
|||
|
|||
GetAccessTokenResultDTO content = new GetAccessTokenResultDTO(); |
|||
content.setAccessToken(token); |
|||
content.setExpireTime(expireTime.getTime()); |
|||
|
|||
return content; |
|||
} |
|||
} |
Loading…
Reference in new issue