序
在springboot项目中结合使用jwt框架对当前登录的用户进行认证,用户信息进行加密返回给前端token
jwt相关博文了解:
具体实现
- 创建一个springboot项目
- 引入jwt核心依赖和相关基本依
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
<!--引入 jwt对java实现的 依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.8</version>
</dependency>
- 创建一个jwt工具类 JwtTokenUtil
public class JwtTokenUtil {
/**
* jwt密匙
*/
private String jwtSecret;
/**
* 默认过期时间
*/
private Long defaultExpiredDate;
public JwtTokenUtil(String jwtSecret, Long defaultExpiredDate) {
this.jwtSecret = jwtSecret;
this.defaultExpiredDate = defaultExpiredDate;
}
/**
* 设置用户jwt对象数据并返回加密token
* @param userId 用户id
* @param claims 存储的参数map
* @return
*/
public String generateToken(String userId, Map<String, Object> claims) {
Date expirationDate = new Date(System.currentTimeMillis() + defaultExpiredDate * 1000L);
Date createdDate = new Date();
return claims == null ? Jwts.builder().setSubject(userId).setIssuedAt(createdDate).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, this.jwtSecret).compact() :
Jwts.builder().setClaims(claims).setSubject(userId).setIssuedAt(createdDate).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, this.jwtSecret).compact();
}
/**
* 通过token获取用户对象数据
* @param token 查询token
* @return
*/
public Claims getClaimFromToken(String token) {
return (Claims)Jwts.parser().setSigningKey(this.jwtSecret).parseClaimsJws(token).getBody();
}
}
注:
1.在实际业务中jwt私钥请妥善保管不要泄露了,不然容易被非法人员获取token并通过私钥进行破解,用户数据将不再安全。
2.在实际业务中jwt私钥(jwtSecret),默认过期时间(defaultExpiredDate),可定义在.properties或.yml类型的配置文件中,并定义@ConfigurationProperties注解类进行映射对应,便于随时修改和维护。
- 创建简单的用户信息实体类 LoginUser
@Data
public class LoginUser {
private Long id;
private String name;
private String pwd;
}
- 创建登录用户上下文类,用于用户的相关信息操作类 LoginContext
public class LoginContext {
//自定义jwt私钥
private static String jwtSecret = "remaindertime";
//jwt过期时间(秒)
private static Long jwtATExpiredDate = 60L;
private JwtTokenUtil jwtTokenUtil;
private static LoginContext loginContext = null;
//通过构造方法创建 jwtTokenUtil 对象
public LoginContext(String jwtSecret,Long jwtATExpiredDate) {
this.jwtTokenUtil = new JwtTokenUtil(jwtSecret, jwtATExpiredDate);
}
//构建 LoginContext 对象
public static LoginContext me() {
if(loginContext == null){
loginContext = new LoginContext(jwtSecret,jwtATExpiredDate);
}
return loginContext;
}
/**
* 登录成功后设置用户数据到jwt中
* @param user 用户对象
* @return 返回token
*/
public String generateToken(LoginUser user) {
HashMap<String, Object> claims = new HashMap();
claims.put("user", user);
return this.jwtTokenUtil.generateToken(user.getId().toString(), claims);
}
/**
* 获取当前登录用户基本信息对象
* @return
*/
public LoginUser getLoginUserNoException() {
String token = this.getCurrentUserTokenReal();
if (token != null) {
currentUser = this.getUserFromToken(token);
return currentUser;
} else {
return null;
}
}
/**
* 通过token解析用户信息对象
* @param token
* @return
*/
public LoginUser getUserFromToken(String token) {
Claims claimFromToken = this.jwtTokenUtil.getClaimFromToken(token);
Map mapUser = claimFromToken.get("user", Map.class);
return BeanUtil.mapToBean(mapUser, LoginUser.class, false);
}
/**
* 获取当前请求下 请求头中的 Authorization 或者 请求参数中 token值
* @return
*/
public String getCurrentUserTokenReal() {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
String token;
if (request != null) {
String authToken = request.getHeader("Authorization");
if (!StringUtils.isEmpty(authToken)) {
return authToken;
}
token = request.getParameter("token");
if (!StringUtils.isEmpty(token)) {
return token;
}
}
return null;
}
}
- 创建controller实现简单的登录和用户信息获取
@RestController public class JwtController { /** * 模拟用户登录接口 * @param userName 账号 * @param pwd 密码 * @return */ @PostMapping(value = "/login") public String login( String userName, String pwd){ //校验登录密码是否正确(略) //... //校验成功,保持用户数据到jwt中,并生成token返回给前端 LoginUser loginUser = new LoginUser(); loginUser.setId(1L); loginUser.setName(userName); String s = LoginContext.me().generateToken(loginUser); return s; } /** * 获取用户信息名称 * @return */ @GetMapping(value = "/getUserInfo") public String getUserInfo(){ //通过用户工具类获取用户信息, LoginUser user = LoginContext.me().getLoginUserNoException(); //用户未登录返回提示 if(user == null){ return "请登录"; } return user.getName(); } }
测试
- 模拟登录,返回 token
- 模拟获取用户信息 将 token设置到请求头中
注:如果在完整的系统中,可把token存储在前端本地,每次请求是都携带该token,后端获取并进行用户登录校验
- token 实现请求(前端获取不到用户信息,并且后端控制台JWT报错)
gtihub地址:前往github获取demo
补充小知识
jwt存在一个明显的弊端,就是用户生成的token不能手动删除,只有等设置的过期时间到了才会自动删除,所以在实际业务使用中,如果修改了用户信息,重新生成token ,但之前生成token不会消失,那么直接用原来的token也能通过。
所以需要结合redis来使用,redis存储token,用户更新信息后删除之前的,更新为当前最新token,当用户请求时,通过校验redis是否存在该请求头中的token,进而判断token是否有效
原创不易,多多一键三连呀!!!