1.添加依赖
首先需要添加jwt对应的依赖。
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2.添加配置
JWT由三部分构成,分别是 header, payload 和 signature, 其中前两部分中的参数涉及到的一些参数需要用户自定义,因此我们需要将这些参数放到配置文件中,这样后续就可以动态修改配置。
# 密钥
jwt.secret = 6L6T5LqG5L2g77yM6LWi5LqG5LiW55WM5Y+I6IO95aaC5L2V44CC
# 签名算法:HS256,HS384,HS512,RS256,RS384,RS512,ES256,ES384,ES512,PS256,PS384,PS512
jwt.header.alg = HS256
#jwt签发者
jwt.payload.iss = picacho
#jwt过期时间(单位:毫秒)
jwt.payload.exp = 60 * 60 * 1000 * 24 * 7
#jwt接收者
jwt.payload.aud = picacho_1
3.创建JWT配置类
创建config包,在该包下创建JWT对应的配置类。
import lombok.Data;
@Data
public class JwtHeader {
private String alg;
private String typ;
}
import lombok.Data;
@Data
public class JwtPayload {
private String iss;
private String exp;
private String sub;
private String aud;
}
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "jwt")
public class JwtProperties {
private String secret;
private JwtHeader header;
private JwtPayload payload;
}
这里通过配置文件的属性值来为jwt进行属性值的配置。
4.创建JWT工具类
创建JWTUtil类,在里面创建generalSecretKey方法来生成密钥,生成的过程需要使用 JWT的方法SecretKeySpec来生成密钥,该密钥在创建JWT和验证 JWT 的时候都会用到且相同。
@Component
public class JWTUtil {
@Resource
JwtProperties jwtProperties;
/**
* 由字符串生成加密key
* @return
*/
private SecretKey generalKey() {
// 本地的密码解码
byte[] encodedKey = Base64.decodeBase64(jwtProperties.getSecret());
// 根据给定的字节数组使用AES加密算法构造一个密钥
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return key;
}
}
创建生成JWT方法。
/**
* 创建jwt
* @param subject
* @return
* @throws Exception
*/
public String createJWT(String subject) throws Exception {
// 生成JWT的时间
long nowTime = System.currentTimeMillis();
Date nowDate = new Date(nowTime);
// 生成签名的时候使用的秘钥secret
SecretKey key = generalKey();
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine se = manager.getEngineByName("js");
int expireTime = 0;
try {
expireTime =(int) se.eval(jwtProperties.getPayload().getExp());
} catch (ScriptException e) {
e.printStackTrace();
}
// 为payload添加各种标准声明和私有声明
DefaultClaims defaultClaims = new DefaultClaims();
defaultClaims.setIssuer(jwtProperties.getPayload().getIss());
defaultClaims.setExpiration(new Date(System.currentTimeMillis() + expireTime));
defaultClaims.setSubject(subject);
defaultClaims.setAudience(jwtProperties.getPayload().getAud());
JwtBuilder builder = Jwts.builder() // 表示new一个JwtBuilder,设置jwt的body
.setClaims(defaultClaims)
.setIssuedAt(nowDate) // iat(issuedAt):jwt的签发时间
.signWith(SignatureAlgorithm.forName(jwtProperties.getHeader().getAlg()), key); // 设置签名,使用的是签名算法和签名使用的秘钥
return builder.compact();
}
创建解密JWT方法。
/**
* 解密jwt
* @param jwt
* @return
* @throws Exception
*/
public Claims parseJWT(String jwt) throws Exception {
SecretKey key = generalKey(); // 签名秘钥,和生成的签名的秘钥一模一样
Claims claims = Jwts.parser() // 得到DefaultJwtParser
.setSigningKey(key) // 设置签名的秘钥
.parseClaimsJws(jwt).getBody(); // 设置需要解析的jwt
return claims;
}
5.实现用户注册
5.1 实现业务层
创建service包,在该包下创建UserService.java类,实现用户注册逻辑。
import com.picacho.common.RestResult;
import com.picacho.entity.User;
public interface UserService {
RestResult<String> registerUser(User user);
}
创建impl包,在该包下创建其实现类。
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Resource
UserMapper userMapper;
@Override
public RestResult<String> registerUser(User user) {
//判断验证码
String telephone = user.getTelephone();
String password = user.getPassword();
if (!StringUtils.hasLength(telephone) || !StringUtils.hasLength(password)){
return RestResult.fail().message("手机号或密码不能为空!");
}
if (isTelePhoneExit(telephone)){
return RestResult.fail().message("手机号已存在!");
}
String salt = UUID.randomUUID().toString().replace("-", "").substring(15);
String passwordAndSalt = password + salt;
String newPassword = DigestUtils.md5DigestAsHex(passwordAndSalt.getBytes());
user.setSalt(salt);
user.setPassword(newPassword);
user.setRegisterTime(DateUtil.getCurrentTime());
int result = userMapper.insert(user);
if (result == 1) {
return RestResult.success();
} else {
return RestResult.fail().message("注册用户失败,请检查输入信息!");
}
}
/**
* 判断手机号是否存在
* @param telePhone
* @return
*/
private boolean isTelePhoneExit(String telePhone) {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getTelephone, telePhone);
List<User> list = userMapper.selectList(lambdaQueryWrapper);
if (list != null && !list.isEmpty()) {
return true;
} else {
return false;
}
}
}
5.2 创建DTO类
在前后端分离项目中,一般前端与后端需要统一数据交互;一般做法是创建DTO类,例如注册功能,这里可以创建RegisterDTO.java,其属性就是前端需要传给后端的数据。
创建dto包,在该包下创建RegisterDTO.java。
import lombok.Data;
@Data
public class RegisterDTO {
private String username;
private String telephone;
private String password;
}
5.3 添加注册接口
@Resource
UserService userService;
@PostMapping(value = "/register")
@ResponseBody
public RestResult<String> register(@RequestBody RegisterDTO registerDTO) {
RestResult<String> restResult = null;
User user = new User();
user.setUsername(registerDTO.getUsername());
user.setTelephone(registerDTO.getTelephone());
user.setPassword(registerDTO.getPassword());
restResult = userService.registerUser(user);
return restResult;
}
5.4 测试效果
启动项目测试效果:
6.实现用户登陆
6.1 实现业务层
RestResult<User> login(User user);
@Override
public RestResult<User> login(User user) {
String telephone = user.getTelephone();
String password = user.getPassword();
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getTelephone, telephone);
User saveUser = userMapper.selectOne(lambdaQueryWrapper);
String salt = saveUser.getSalt();
String passwordAndSalt = password + salt;
String newPassword = DigestUtils.md5DigestAsHex(passwordAndSalt.getBytes());
if (newPassword.equals(saveUser.getPassword())) {
saveUser.setPassword("");
saveUser.setSalt("");
return RestResult.success().data(saveUser);
} else {
return RestResult.fail().message("手机号或密码错误!");
}
}
6.2 创建VO类
import lombok.Data;
@Data
public class LoginVO {
private String username;
private String token;
}
6.3 添加登陆接口
@Resource
JWTUtil jwtUtil;
@GetMapping(value = "/login")
@ResponseBody
public RestResult<LoginVO> userLogin(String telephone, String password) {
RestResult<LoginVO> restResult = new RestResult<LoginVO>();
LoginVO loginVO = new LoginVO();
User user = new User();
user.setTelephone(telephone);
user.setPassword(password);
RestResult<User> loginResult = userService.login(user);
if (!loginResult.getSuccess()) {
return RestResult.fail().message("登录失败!");
}
loginVO.setUsername(loginResult.getData().getUsername());
String jwt = "";
try {
ObjectMapper objectMapper = new ObjectMapper();
jwt = jwtUtil.createJWT(objectMapper.writeValueAsString(loginResult.getData()));
} catch (Exception e) {
return RestResult.fail().message("登录失败!");
}
loginVO.setToken(jwt);
return RestResult.success().data(loginVO);
}
6.4 测试效果
启动项目测试效果:
这里返回了token,后面就可以通过携带token来访问需要登陆才能有权限访问的路径。
7.验证token
@GetMapping("/checkToken")
@ResponseBody
public RestResult<User> checkToken(@RequestHeader("token") String token) {
RestResult<User> restResult = new RestResult<User>();
User tokenUserInfo = null;
try {
Claims c = jwtUtil.parseJWT(token);
String subject = c.getSubject();
ObjectMapper objectMapper = new ObjectMapper();
tokenUserInfo = objectMapper.readValue(subject, User.class);
} catch (Exception e) {
return RestResult.fail().message("认证失败");
}
if (tokenUserInfo != null) {
return RestResult.success().data(tokenUserInfo);
} else {
return RestResult.fail().message("用户暂未登录");
}
}
7.1 测试效果
启动项目,携带token测试效果。
可以看到验证成功了。
项目demo源码下载地址:源码下载