package com.sunyard.chsm.service; import com.sunyard.chsm.auth.AppUser; import com.sunyard.chsm.constant.SecurityConstant; import com.sunyard.chsm.enums.EnableStatus; import com.sunyard.chsm.mapper.ApplicationMapper; import com.sunyard.chsm.model.entity.Application; import com.sunyard.chsm.param.AppTokenReq; import com.sunyard.chsm.param.AppTokenResp; import com.sunyard.chsm.utils.CodecUtils; import com.sunyard.chsm.utils.gm.BCSM3Utils; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Header; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.util.Assert; import javax.annotation.Resource; import javax.validation.Valid; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.UUID; /** * @author liulu * @since 2024/12/5 */ @Slf4j @Service public class AppLoginService { /** * token 过期时间, 分钟 */ @Value("${chsm.token.expireTime:720}") private Integer tokenExpireTime; @Resource private ApplicationMapper applicationMapper; public AppTokenResp getAppToken(AppTokenReq req) { Long random = req.getRandom(); long now = System.currentTimeMillis(); Assert.isTrue(now - random <= 5 * 60 * 1000, "请求已超时"); String appKey = req.getAppKey(); Application application = applicationMapper.selectByAppKey(appKey); Assert.isTrue(EnableStatus.ENABLED.getCode().equals(application.getStatus()), "此应用已停用"); String data = appKey + random + application.getAppSecret(); byte[] hmac = BCSM3Utils.hmac(application.getAppSecret().getBytes(), data.getBytes()); String serverHmac = CodecUtils.encodeHex(hmac); if (!Objects.equals(req.getHmac(), serverHmac)) { log.warn("appKey: {}, req hmac: {}, server hmac: {}", appKey, req.getHmac(), serverHmac); throw new IllegalArgumentException("应用认证失败"); } AppTokenResp resp = new AppTokenResp(); resp.setToken(genToken(application)); return resp; } private String genToken(Application app) { Map claims = new HashMap<>(); claims.put("appId", app.getId()); claims.put("name", app.getName()); claims.put("serviceIds", Collections.singletonList(app.getId())); Date now = new Date(); return Jwts.builder() .setHeaderParam(Header.TYPE, Header.JWT_TYPE) .setClaims(claims) .setIssuedAt(now) .setExpiration(new Date(now.getTime() + tokenExpireTime * 60 * 1000)) .setId(UUID.randomUUID().toString()) .signWith(SignatureAlgorithm.HS512, SecurityConstant.JWT_SIGN_KEY) .compact(); } public AppTokenResp getAppTokenForTest(@Valid AppTokenReq req) { // Long random = req.getRandom(); // long now = System.currentTimeMillis(); // Assert.isTrue(now - random <= 5 * 60 * 1000, "请求已超时"); Application application = applicationMapper.selectByAppKey(req.getAppKey()); Assert.isTrue(EnableStatus.ENABLED.getCode().equals(application.getStatus()), "此应用已停用"); if (!Objects.equals(req.getHmac(), application.getAppSecret())) { log.warn("appKey: {}, req hmac: {},", req.getAppKey(), req.getHmac()); throw new IllegalArgumentException("应用认证失败"); } AppTokenResp resp = new AppTokenResp(); resp.setToken(genToken(application)); return resp; } public AppUser verifyToken(String token) { Claims claims = Jwts.parser() .setSigningKey(SecurityConstant.JWT_SIGN_KEY) .parseClaimsJws(token) .getBody(); AppUser user = new AppUser(); user.setAppId(claims.get("appId", Long.class)); user.setName(claims.get("name", String.class)); user.setServiceIds(claims.get("serviceIds", List.class)); return user; } }