Browse Source

api token初步开发

jwy
review512jwy@163.com 6 days ago
parent
commit
46f55403da
  1. 7
      data-center-business-controller/src/main/java/com/techsor/datacenter/business/configurator/ApiConfig.java
  2. 57
      data-center-business-controller/src/main/java/com/techsor/datacenter/business/configurator/interceptor/ApiTokenInterceptor.java
  3. 9
      data-center-business-controller/src/main/java/com/techsor/datacenter/business/configurator/interceptor/ApiTokenRequired.java
  4. 10
      data-center-business-controller/src/main/java/com/techsor/datacenter/business/controller/CommonController.java
  5. 2
      data-center-business-controller/src/main/resources/config/application.properties
  6. 12
      data-center-business-model/src/main/java/com/techsor/datacenter/business/vo/common/ApiTokenVO.java
  7. 12
      data-center-business-model/src/main/java/com/techsor/datacenter/business/vo/common/RedisApiTokenInfo.java
  8. 10
      data-center-business-service/src/main/java/com/techsor/datacenter/business/service/ApiAuthService.java
  9. 59
      data-center-business-service/src/main/java/com/techsor/datacenter/business/service/impl/ApiAuthServiceImpl.java
  10. 19
      data-center-business-util/src/main/java/com/techsor/datacenter/business/util/ApiContext.java
  11. 109
      data-center-business-util/src/main/java/com/techsor/datacenter/business/util/SimpleJwtTokenUtil.java

7
data-center-business-controller/src/main/java/com/techsor/datacenter/business/configurator/ApiConfig.java

@ -1,5 +1,6 @@
package com.techsor.datacenter.business.configurator; package com.techsor.datacenter.business.configurator;
import com.techsor.datacenter.business.configurator.interceptor.ApiTokenInterceptor;
import org.springframework.boot.web.servlet.MultipartConfigFactory; import org.springframework.boot.web.servlet.MultipartConfigFactory;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -24,6 +25,11 @@ public class ApiConfig implements WebMvcConfigurer {
public AccessApiInterceptor accessApiInterceptor(){ public AccessApiInterceptor accessApiInterceptor(){
return new AccessApiInterceptor(); return new AccessApiInterceptor();
} }
@Bean
public ApiTokenInterceptor apiTokenInterceptor(){
return new ApiTokenInterceptor();
}
/** /**
* 加了配置spring.web.resources.add-mappings=false * 加了配置spring.web.resources.add-mappings=false
@ -42,6 +48,7 @@ public class ApiConfig implements WebMvcConfigurer {
@Override @Override
public void addInterceptors(InterceptorRegistry registry){ public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(accessApiInterceptor()); registry.addInterceptor(accessApiInterceptor());
registry.addInterceptor(apiTokenInterceptor());
} }
@Bean @Bean

57
data-center-business-controller/src/main/java/com/techsor/datacenter/business/configurator/interceptor/ApiTokenInterceptor.java

@ -0,0 +1,57 @@
package com.techsor.datacenter.business.configurator.interceptor;
import com.alibaba.fastjson2.JSON;
import com.techsor.datacenter.business.util.ApiContext;
import com.techsor.datacenter.business.util.redis.RedisUtil;
import com.techsor.datacenter.business.vo.common.RedisApiTokenInfo;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import java.util.Map;
@Component
public class ApiTokenInterceptor implements HandlerInterceptor {
@Autowired
private RedisUtil redisUtil;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String auth = request.getHeader("Authorization");
if (auth == null || !auth.startsWith("Bearer ")) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
String token = auth.substring(7);
String redisKey = "api:token:" + token;
Object tokenInfo = redisUtil.get(redisKey);
if (null == tokenInfo){
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
RedisApiTokenInfo redisApiTokenInfo = JSON.parseObject(tokenInfo.toString(), RedisApiTokenInfo.class);
Long companyId = redisApiTokenInfo.getTopCompanyId();
ApiContext.setCompanyId(companyId);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
ApiContext.clear();
}
}

9
data-center-business-controller/src/main/java/com/techsor/datacenter/business/configurator/interceptor/ApiTokenRequired.java

@ -0,0 +1,9 @@
package com.techsor.datacenter.business.configurator.interceptor;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiTokenRequired {
}

10
data-center-business-controller/src/main/java/com/techsor/datacenter/business/controller/CommonController.java

@ -6,6 +6,8 @@ import com.techsor.datacenter.business.configurator.interceptor.AccessRequired;
import com.techsor.datacenter.business.dto.common.RepostRoidParams; import com.techsor.datacenter.business.dto.common.RepostRoidParams;
import com.techsor.datacenter.business.dto.common.api.*; import com.techsor.datacenter.business.dto.common.api.*;
import com.techsor.datacenter.business.dto.common.roidproblemreport.ProblemReportsSummariesSearchParams; import com.techsor.datacenter.business.dto.common.roidproblemreport.ProblemReportsSummariesSearchParams;
import com.techsor.datacenter.business.service.ApiAuthService;
import com.techsor.datacenter.business.vo.common.ApiTokenVO;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -60,6 +62,8 @@ public class CommonController{
@Autowired @Autowired
private CommonService commonService; private CommonService commonService;
@Autowired
private ApiAuthService apiAuthService;
@Value("${open.api.mock}") @Value("${open.api.mock}")
private String openApiMock; private String openApiMock;
@ -393,4 +397,10 @@ public class CommonController{
@Parameter(name = "Apikey", description = "API key", required = false, schema = @Schema(defaultValue = "123456")) @RequestHeader(required = true) String Apikey) { @Parameter(name = "Apikey", description = "API key", required = false, schema = @Schema(defaultValue = "123456")) @RequestHeader(required = true) String Apikey) {
return commonService.queryCancelAlarmDevice(null, Apikey, apiAlarmDeviceSearchParams); return commonService.queryCancelAlarmDevice(null, Apikey, apiAlarmDeviceSearchParams);
} }
@PostMapping("/auth/token")
public SimpleDataResponse<ApiTokenVO> getToken(
@RequestHeader("X-API-KEY") String apiKey) {
return apiAuthService.generateToken(apiKey);
}
} }

2
data-center-business-controller/src/main/resources/config/application.properties

@ -33,7 +33,7 @@ logging_appender=${loggingAppender:STDOUT}
logging_maxHistory=30 logging_maxHistory=30
logging_maxFileSize=100MB logging_maxFileSize=100MB
user.login.keytimeout=3600 user.login.keytimeout=43200
#集群模式cluster #集群模式cluster
spring.redis.cluster.nodes=192.168.0.30:7000,192.168.0.30:7001 spring.redis.cluster.nodes=192.168.0.30:7000,192.168.0.30:7001

12
data-center-business-model/src/main/java/com/techsor/datacenter/business/vo/common/ApiTokenVO.java

@ -0,0 +1,12 @@
package com.techsor.datacenter.business.vo.common;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class ApiTokenVO {
private String token;
private Long expireTime;
}

12
data-center-business-model/src/main/java/com/techsor/datacenter/business/vo/common/RedisApiTokenInfo.java

@ -0,0 +1,12 @@
package com.techsor.datacenter.business.vo.common;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class RedisApiTokenInfo {
private String apiKey;
private Long topCompanyId;
}

10
data-center-business-service/src/main/java/com/techsor/datacenter/business/service/ApiAuthService.java

@ -0,0 +1,10 @@
package com.techsor.datacenter.business.service;
import com.techsor.datacenter.business.common.response.SimpleDataResponse;
import com.techsor.datacenter.business.vo.common.ApiTokenVO;
public interface ApiAuthService {
SimpleDataResponse<ApiTokenVO> generateToken(String apiKey);
}

59
data-center-business-service/src/main/java/com/techsor/datacenter/business/service/impl/ApiAuthServiceImpl.java

@ -0,0 +1,59 @@
package com.techsor.datacenter.business.service.impl;
import com.alibaba.fastjson2.JSON;
import com.techsor.datacenter.business.common.config.DataSourceInterceptor;
import com.techsor.datacenter.business.common.response.ResponseCode;
import com.techsor.datacenter.business.common.response.SimpleDataResponse;
import com.techsor.datacenter.business.dao.ex.BasicCompanyMapperExt;
import com.techsor.datacenter.business.service.ApiAuthService;
import com.techsor.datacenter.business.util.SimpleJwtTokenUtil;
import com.techsor.datacenter.business.util.redis.RedisUtil;
import com.techsor.datacenter.business.vo.common.ApiTokenVO;
import com.techsor.datacenter.business.vo.common.RedisApiTokenInfo;
import com.techsor.datacenter.business.vo.company.ApikeyInfo2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@Service
public class ApiAuthServiceImpl implements ApiAuthService {
private static final long TOKEN_EXPIRE_SECONDS = 12 * 60 * 60;
@Autowired
RedisUtil redisUtil;
@Autowired
private BasicCompanyMapperExt basicCompanyMapperExt;
@Autowired
private DataSourceInterceptor dataSourceInterceptor;
@Override
public SimpleDataResponse<ApiTokenVO> generateToken(String apiKey) {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("apikey", apiKey);
ApikeyInfo2 selfInfo = basicCompanyMapperExt.getAuroraInfoByApikey(paramMap);
if (selfInfo == null) {
return SimpleDataResponse.fail(ResponseCode.MSG_ERROR, "Invalid API Key");
}
Long companyId = selfInfo.getId();
Long topCompanyId = dataSourceInterceptor.getTopCompanyId(companyId+"");
String token = "Bearer " + SimpleJwtTokenUtil.generate();
String redisKey = "api:token:" + token;
redisUtil.set(redisKey, JSON.toJSONString(new RedisApiTokenInfo(apiKey, topCompanyId)));
redisUtil.expire(redisKey, TOKEN_EXPIRE_SECONDS);
return SimpleDataResponse.success(new ApiTokenVO(token, TOKEN_EXPIRE_SECONDS));
}
}

19
data-center-business-util/src/main/java/com/techsor/datacenter/business/util/ApiContext.java

@ -0,0 +1,19 @@
package com.techsor.datacenter.business.util;
public class ApiContext {
private static final ThreadLocal<Long> COMPANY_ID_HOLDER = new ThreadLocal<>();
public static void setCompanyId(Long companyId) {
COMPANY_ID_HOLDER.set(companyId);
}
public static Long getCompanyId() {
return COMPANY_ID_HOLDER.get();
}
public static void clear() {
COMPANY_ID_HOLDER.remove();
}
}

109
data-center-business-util/src/main/java/com/techsor/datacenter/business/util/SimpleJwtTokenUtil.java

@ -0,0 +1,109 @@
package com.techsor.datacenter.business.util;
import org.springframework.stereotype.Component;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;
public class SimpleJwtTokenUtil {
private static final String HMAC_ALGO = "HmacSHA256";
private static final String subject = "company-from-Japan-aeon";
private static final String secret = "ci3b512jwy1949pla";
private SimpleJwtTokenUtil() {
}
public static String generate() {
return generate(subject, secret);
}
/**
* 生成一个 JWT 格式的 token无过期
*
* @param subject 业务唯一标识userId / clientId / appId
* @param secret 签名密钥
*/
public static String generate(String subject, String secret) {
// header 固定
String headerJson = "{\"alg\":\"HS256\",\"typ\":\"JWT\"}";
// payload 只放你需要的最小信息
String payloadJson = String.format("{\"sub\":\"%s\"}", subject);
String header = base64Url(headerJson.getBytes(StandardCharsets.UTF_8));
String payload = base64Url(payloadJson.getBytes(StandardCharsets.UTF_8));
String data = header + "." + payload;
String signature = base64Url(hmac(secret, data));
return data + "." + signature;
}
/**
* 校验 token 是否被伪造只校验签名
*/
public static boolean verify(String token, String secret) {
String[] parts = token.split("\\.");
if (parts.length != 3) {
return false;
}
String data = parts[0] + "." + parts[1];
String expectedSig = base64Url(hmac(secret, data));
return MessageDigest.isEqual(
expectedSig.getBytes(StandardCharsets.UTF_8),
parts[2].getBytes(StandardCharsets.UTF_8)
);
}
/**
* 解析 subject不做合法性校验
*/
public static String getSubject(String token) {
String[] parts = token.split("\\.");
if (parts.length != 3) {
return null;
}
String json = new String(
Base64.getUrlDecoder().decode(parts[1]),
StandardCharsets.UTF_8
);
// {"sub":"xxx"}
int start = json.indexOf(":\"") + 2;
int end = json.lastIndexOf("\"");
return start > 1 && end > start ? json.substring(start, end) : null;
}
/* ================= 内部方法 ================= */
private static byte[] hmac(String secret, String data) {
try {
Mac mac = Mac.getInstance(HMAC_ALGO);
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), HMAC_ALGO));
return mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
throw new RuntimeException("HMAC error", e);
}
}
private static String base64Url(byte[] bytes) {
return Base64.getUrlEncoder()
.withoutPadding()
.encodeToString(bytes);
}
public static void main(String[] args) {
System.out.println(SimpleJwtTokenUtil.generate());
}
}
Loading…
Cancel
Save