forked from luyan/epmet-cloud-lingshan
				
			
				 283 changed files with 4321 additions and 85 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"); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
} | 
				
			|||
Some files were not shown because too many files changed in this diff
					Loading…
					
					
				
		Reference in new issue