mirror of
https://gitee.com/youlaitech/youlai-mall.git
synced 2024-12-22 20:54:26 +08:00
add生成JWT Token工具类
This commit is contained in:
parent
6672bbc8cd
commit
df640c4e18
@ -0,0 +1,29 @@
|
||||
package com.youlai.auth.config;
|
||||
|
||||
import com.youlai.auth.jwt.JwtProperties;
|
||||
import com.youlai.auth.jwt.JwtTokenGenerator;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* JwtConfiguration
|
||||
*/
|
||||
@EnableConfigurationProperties(JwtProperties.class)
|
||||
@ConditionalOnProperty(prefix = "jwt.config", name = "enabled")
|
||||
@Configuration
|
||||
public class JwtConfiguration {
|
||||
|
||||
/**
|
||||
* Jwt token generator.
|
||||
*
|
||||
* @param jwtProperties the jwt properties
|
||||
* @return the jwt token generator
|
||||
*/
|
||||
@Bean
|
||||
public JwtTokenGenerator jwtTokenGenerator(JwtProperties jwtProperties) {
|
||||
return new JwtTokenGenerator(jwtProperties);
|
||||
}
|
||||
|
||||
}
|
@ -4,6 +4,7 @@ import cn.hutool.json.JSONObject;
|
||||
import com.nimbusds.jose.jwk.JWKSet;
|
||||
import com.nimbusds.jose.jwk.RSAKey;
|
||||
import com.youlai.auth.enums.OAuthClientEnum;
|
||||
import com.youlai.auth.jwt.JwtTokenPair;
|
||||
import com.youlai.auth.service.WeAppService;
|
||||
import com.youlai.common.constant.AuthConstants;
|
||||
import com.youlai.common.result.Result;
|
||||
@ -15,7 +16,6 @@ import io.swagger.annotations.ApiOperation;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@ -53,7 +53,7 @@ public class OAuthController {
|
||||
@ApiIgnore Principal principal,
|
||||
@ApiIgnore @RequestParam Map<String, String> parameters
|
||||
) throws HttpRequestMethodNotSupportedException {
|
||||
OAuth2AccessToken oAuth2AccessToken;
|
||||
JwtTokenPair jwtTokenPair = new JwtTokenPair();
|
||||
|
||||
/**
|
||||
* 获取登录认证的客户端ID
|
||||
@ -67,15 +67,15 @@ public class OAuthController {
|
||||
|
||||
switch (client) {
|
||||
case WEAPP: // 微信小程序
|
||||
oAuth2AccessToken = weAppService.login(principal, parameters);
|
||||
jwtTokenPair = weAppService.login(parameters);
|
||||
break;
|
||||
case TEST: // knife4j接口测试文档使用 client_id/client_secret : client/123456
|
||||
return tokenEndpoint.postAccessToken(principal, parameters).getBody();
|
||||
default:
|
||||
oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
|
||||
// oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
|
||||
break;
|
||||
}
|
||||
return Result.success(oAuth2AccessToken);
|
||||
return Result.success(jwtTokenPair);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "注销", notes = "logout")
|
||||
|
@ -0,0 +1,102 @@
|
||||
package com.youlai.auth.jwt;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 构建 jwt payload
|
||||
**/
|
||||
public class JwtPayloadBuilder {
|
||||
|
||||
private Map<String, Object> payload = new HashMap<>();
|
||||
/**
|
||||
* 附加的属性
|
||||
*/
|
||||
private Map<String, String> additional;
|
||||
/**
|
||||
* jwt签发者
|
||||
**/
|
||||
private String iss;
|
||||
/**
|
||||
* jwt所面向的用户
|
||||
**/
|
||||
private String sub;
|
||||
/**
|
||||
* 接收jwt的一方
|
||||
**/
|
||||
private String aud;
|
||||
/**
|
||||
* jwt的过期时间,这个过期时间必须要大于签发时间
|
||||
**/
|
||||
private LocalDateTime exp;
|
||||
/**
|
||||
* jwt的签发时间
|
||||
**/
|
||||
private LocalDateTime iat = LocalDateTime.now();
|
||||
/**
|
||||
* 权限集
|
||||
*/
|
||||
private Set<String> authorities = new HashSet<>();
|
||||
/**
|
||||
* jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
|
||||
**/
|
||||
private String jti = IdUtil.simpleUUID();
|
||||
|
||||
public JwtPayloadBuilder iss(String iss) {
|
||||
this.iss = iss;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public JwtPayloadBuilder sub(String sub) {
|
||||
this.sub = sub;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JwtPayloadBuilder aud(String aud) {
|
||||
this.aud = aud;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JwtPayloadBuilder authorities(Set<String> authorities) {
|
||||
this.authorities = authorities;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JwtPayloadBuilder expDays(int days) {
|
||||
Assert.isTrue(days > 0, "jwt expireDate must after now");
|
||||
this.exp = this.iat.plusDays(days);
|
||||
return this;
|
||||
}
|
||||
|
||||
public JwtPayloadBuilder additional(Map<String, String> additional) {
|
||||
this.additional = additional;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String builder() {
|
||||
payload.put("iss", this.iss);
|
||||
payload.put("sub", this.sub);
|
||||
payload.put("aud", this.aud);
|
||||
payload.put("exp", this.exp.toEpochSecond(ZoneOffset.of("+8")));
|
||||
payload.put("iat", this.iat.toEpochSecond(ZoneOffset.of("+8")));
|
||||
payload.put("jti", this.jti);
|
||||
|
||||
if (!CollectionUtils.isEmpty(additional)) {
|
||||
payload.putAll(additional);
|
||||
}
|
||||
payload.put("authorities", this.authorities.toArray());
|
||||
return JSONUtil.toJsonStr(JSONUtil.parse(payload));
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package com.youlai.auth.jwt;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Jwt 在 springboot application.yml 中的配置文件
|
||||
*/
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "jwt.config")
|
||||
public class JwtProperties {
|
||||
/**
|
||||
* 是否可用
|
||||
*/
|
||||
private boolean enabled;
|
||||
/**
|
||||
* jks 路径
|
||||
*/
|
||||
private String keyLocation;
|
||||
/**
|
||||
* key alias
|
||||
*/
|
||||
private String keyAlias;
|
||||
/**
|
||||
* key store pass
|
||||
*/
|
||||
private String keyPass;
|
||||
/**
|
||||
* jwt签发者
|
||||
**/
|
||||
private String iss;
|
||||
/**
|
||||
* jwt所面向的用户
|
||||
**/
|
||||
private String sub;
|
||||
/**
|
||||
* access jwt token 有效天数
|
||||
*/
|
||||
private int accessExpDays;
|
||||
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
package com.youlai.auth.jwt;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.jwt.Jwt;
|
||||
import org.springframework.security.jwt.JwtHelper;
|
||||
import org.springframework.security.jwt.crypto.sign.RsaSigner;
|
||||
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
|
||||
import org.springframework.security.jwt.crypto.sign.SignatureVerifier;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* JwtTokenGenerator
|
||||
*/
|
||||
@Slf4j
|
||||
public class JwtTokenGenerator {
|
||||
private static final String JWT_EXP_KEY = "exp";
|
||||
private KeyPair keyPair;
|
||||
private JwtPayloadBuilder jwtPayloadBuilder = new JwtPayloadBuilder();
|
||||
private JwtProperties jwtProperties;
|
||||
|
||||
/**
|
||||
* Instantiates a new Jwt token generator.
|
||||
*
|
||||
* @param jwtProperties the jwt properties
|
||||
*/
|
||||
public JwtTokenGenerator(JwtProperties jwtProperties) {
|
||||
this.jwtProperties = jwtProperties;
|
||||
|
||||
KeyPairFactory keyPairFactory = new KeyPairFactory();
|
||||
this.keyPair = keyPairFactory.getKeyPair(jwtProperties);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Jwt token pair jwt token pair.
|
||||
*
|
||||
* @param aud the aud
|
||||
* @param authorities the authorities
|
||||
* @param additional the additional
|
||||
* @return the jwt token pair
|
||||
*/
|
||||
public JwtTokenPair jwtTokenPair(String aud, Set<String> authorities, Map<String, String> additional) {
|
||||
String accessToken = jwtToken(aud, jwtProperties.getAccessExpDays(), authorities, additional);
|
||||
|
||||
JwtTokenPair jwtTokenPair = new JwtTokenPair();
|
||||
jwtTokenPair.setToken_type("bearer");
|
||||
jwtTokenPair.setAccess_token(accessToken);
|
||||
return jwtTokenPair;
|
||||
}
|
||||
|
||||
/**
|
||||
* Jwt token string.
|
||||
*
|
||||
* @param aud the aud
|
||||
* @param exp the exp
|
||||
* @param authorities the authorities
|
||||
* @param additional the additional
|
||||
* @return the string
|
||||
*/
|
||||
private String jwtToken(String aud, int exp, Set<String> authorities, Map<String, String> additional) {
|
||||
String payload = jwtPayloadBuilder
|
||||
.iss(jwtProperties.getIss())
|
||||
.sub(jwtProperties.getSub())
|
||||
.aud(aud)
|
||||
.additional(additional)
|
||||
.authorities(authorities)
|
||||
.expDays(exp)
|
||||
.builder();
|
||||
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
|
||||
|
||||
RsaSigner signer = new RsaSigner(privateKey);
|
||||
return JwtHelper.encode(payload, signer).getEncoded();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解码 并校验签名 过期不予解析
|
||||
*
|
||||
* @param jwtToken the jwt token
|
||||
* @return the jwt claims
|
||||
*/
|
||||
public JSONObject decodeAndVerify(String jwtToken) {
|
||||
Assert.hasText(jwtToken, "jwt token must not be bank");
|
||||
RSAPublicKey rsaPublicKey = (RSAPublicKey) this.keyPair.getPublic();
|
||||
SignatureVerifier rsaVerifier = new RsaVerifier(rsaPublicKey);
|
||||
Jwt jwt = JwtHelper.decodeAndVerify(jwtToken, rsaVerifier);
|
||||
String claims = jwt.getClaims();
|
||||
JSONObject jsonObject = JSONUtil.parseObj(claims);
|
||||
String exp = jsonObject.getStr(JWT_EXP_KEY);
|
||||
|
||||
if (isExpired(exp)) {
|
||||
throw new IllegalStateException("jwt token is expired");
|
||||
}
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断jwt token是否过期.
|
||||
*
|
||||
* @param exp the jwt token exp
|
||||
* @return the boolean
|
||||
*/
|
||||
private boolean isExpired(String exp) {
|
||||
return LocalDateTime.now().isAfter(LocalDateTime.ofEpochSecond(Long.parseLong(exp), 0, ZoneOffset.ofHours(8)));
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.youlai.auth.jwt;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* JwtTokenPair
|
||||
*
|
||||
**/
|
||||
@Data
|
||||
public class JwtTokenPair implements Serializable {
|
||||
private static final long serialVersionUID = -8518897818107784049L;
|
||||
private String access_token;
|
||||
private String token_type;
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.youlai.auth.jwt;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
|
||||
|
||||
import java.security.KeyPair;
|
||||
|
||||
/**
|
||||
* KeyPairFactory
|
||||
**/
|
||||
public class KeyPairFactory {
|
||||
|
||||
public KeyPair getKeyPair(JwtProperties jwtProperties) {
|
||||
KeyStoreKeyFactory factory = new KeyStoreKeyFactory(new ClassPathResource(jwtProperties.getKeyLocation()),
|
||||
jwtProperties.getKeyPass().toCharArray());
|
||||
KeyPair keyPair = factory.getKeyPair(jwtProperties.getKeyAlias(), jwtProperties.getKeyPass().toCharArray());
|
||||
return keyPair;
|
||||
}
|
||||
}
|
@ -4,8 +4,9 @@ import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.youlai.auth.jwt.JwtTokenGenerator;
|
||||
import com.youlai.auth.jwt.JwtTokenPair;
|
||||
import com.youlai.auth.enums.PasswordEncoderTypeEnum;
|
||||
import com.youlai.common.constant.AuthConstants;
|
||||
import com.youlai.common.constant.GlobalConstants;
|
||||
import com.youlai.common.result.Result;
|
||||
import com.youlai.common.result.ResultCode;
|
||||
@ -17,11 +18,13 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.security.Principal;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@ -37,16 +40,17 @@ public class WeAppService {
|
||||
private PasswordEncoder passwordEncoder;
|
||||
private TokenEndpoint tokenEndpoint;
|
||||
|
||||
@Resource
|
||||
private JwtTokenGenerator jwtTokenGenerator;
|
||||
|
||||
/**
|
||||
* @param principal
|
||||
* @param parameters code=小程序授权code
|
||||
* encryptedData=包括敏感数据在内的完整用户信息的加密数据
|
||||
* iv=加密算法的初始向量
|
||||
* @return
|
||||
*/
|
||||
@SneakyThrows
|
||||
public OAuth2AccessToken login(Principal principal, Map<String, String> parameters) {
|
||||
public JwtTokenPair login(Map<String, String> parameters) {
|
||||
|
||||
String code = parameters.get("code");
|
||||
|
||||
@ -61,6 +65,7 @@ public class WeAppService {
|
||||
String sessionKey = session.getSessionKey();
|
||||
|
||||
Result<AuthMemberDTO> result = memberFeignClient.getUserByOpenid(openid);
|
||||
Long userId = result.getData().getId();
|
||||
|
||||
if (ResultCode.USER_NOT_EXIST.getCode().equals(result.getCode())) { // 微信授权登录 会员信息不存在时 注册会员
|
||||
String encryptedData = parameters.get("encryptedData");
|
||||
@ -86,11 +91,9 @@ public class WeAppService {
|
||||
}
|
||||
}
|
||||
|
||||
// oauth2认证参数对应授权登录时注册会员的username、password信息,模拟通过oauth2的密码模式认证生成JWT
|
||||
parameters.put("username", openid);
|
||||
parameters.put("password", openid);
|
||||
|
||||
OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
|
||||
return oAuth2AccessToken;
|
||||
HashSet<String> roles = new HashSet<>();
|
||||
HashMap<String, String> additional = new HashMap<>();
|
||||
additional.put("userId", String.valueOf(userId));
|
||||
return jwtTokenGenerator.jwtTokenPair(openid, roles, additional);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user