jwt的简单使用
简单做一点记录,写得比较乱
优点:无状态(不用存储)、跨域(估计可以放在地址栏)、比redis快(直接当场解析)
缺点: 在颁发后无法让JWT即刻失效。例如改密码之类的操作,无法强制让该用户所有登录态失效。所以对于一些重要的接口,应该进行二次认证,用黑名单解决。
流程总结:
每次一登录成功,立刻用工具类生成令牌返回给前端(一般会把用户的一些信息作为原料制作令牌),前端需要访问受保护的页面时带令牌过来(可以在请求头、请求行、cookie中),用拦截器预先解析令牌得到claim对象,就用这个对象完成权限认证
代码
判断登陆是否成功是手动的比对密码,登陆成功就获得令牌。以后想保护那个路径,就在那个路径下面获取令牌,并且手动比对,正确后才能继续操作,否则就返回“没有权限”,这样就实现了手动保护。
/**
* 登陆
*/
@RequestMapping(value = "/login", method = RequestMethod.POST)
public Result login(@RequestBody Map<String, String> userMap) {
Admin admin = adminService.login(userMap);
if (admin == null) {
return new Result(false, StatusCode.LOGINERROR, "登录失败");
}
//登陆成功之后生成令牌,这里的admin最好是把查出来的用户的实际角色传过去,不要写死
String token = jwtUtil.createJWT(admin.getId(), admin.getLoginname(), "admin");
Map<String,Object> map = new HashMap<>();
map.put("admin",admin);
map.put("token",token);
return new Result(true, StatusCode.OK, "登录成功", map);
}
手动验证密码,保持存取一致就行了
/**
* 管理员登录
* @param userMap
* @return
*/
public Admin login(Map<String, String> userMap) {
String loginname = userMap.get("loginname");
String password = userMap.get("password");
if(StringUtils.isEmpty(loginname)||StringUtils.isEmpty(password)){
return null;
}
//根据手机号码查询用户对象
Admin admin= adminDao.findByLoginname(loginname);
if(admin == null){
return null;
}
//CharSequence rawPassword:用户登录页面输入的密码 String encodedPassword数据库中查询出来加密的密码
if(encoder.matches(password,admin.getPassword())){
return admin;
}
return null;
}
生成令牌,工具类的完整代码放在最后面
/**
* 生成JWT
*生成令牌需要加点素材,加多少其实并没有具体的规定,一般加点用户信息和当前时间就行,这样还原的时候顺便能得到用户信息,
* 有了这些素材就能生成加密的字符串了,一定要加盐保证安全性
*
* @param id 当前登录用户的id
* @param subject 登录用户名
* @param roles admin user 角色
*
*/
public String createJWT(String id, String subject, String roles) {
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
JwtBuilder builder = Jwts.builder().setId(id)
.setSubject(subject)
.setIssuedAt(now)
//注意,这里的claim相当于自定义键值对
.signWith(SignatureAlgorithm.HS256, key).claim("roles", roles);
if (ttl > 0) {
//设置过期时间,也可以不设置。注意,如果解析时过期了会报错,最好try起来
builder.setExpiration( new Date( nowMillis + ttl));
}
return builder.compact();
}
用户得到令牌后,在访问重点路径的时候,让前端带过来
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public Result delete(@PathVariable String id) {
//这里用了拦截器,在拦截器那里已经解析好了,这里直接得到claims
Claims claims = (Claims)request.getAttribute("admin_claims");
if(claims == null || !claims.get("roles").equals("admin")){
return new Result(false, StatusCode.ACCESSERROR, "权限不足");
}
/*//先验证请求头中token信息 key:Authorization value:Bearer+空格+token
String headToken = request.getHeader("Authorization");//请求头token key定义格式 格式校验
if(StringUtils.isEmpty(headToken) || !headToken.startsWith("Bearer ")){
return new Result(false, StatusCode.ACCESSERROR, "权限不足");
}
//截取head中token
String realToken = headToken.substring(7);
//验证失败 返回权限不足
Claims claims = jwtUtil.parseJWT(realToken);
if(claims == null || !claims.get("roles").equals("admin")){
return new Result(false, StatusCode.ACCESSERROR, "权限不足");
}*/
//验证通过,则执行删除用户
userService.deleteById(id);
return new Result(true, StatusCode.OK, "删除成功");
}
工具类完整代码,使用JWT需要导入jar包
/**
* jwt工具类
* 认证创建token createJWT
* 鉴权解析token parseJWT
* jwt
* config
* key :itcast
* ttl 60*60*1000 = 3600000
*/
@ConfigurationProperties("jwt.config") //@Value
public class JwtUtil {
/**
* 秘钥,相当于盐,从配置文件中读取
*/
private String key ;
/**
* 过期时间,从配置文件中读取
*/
private long ttl ;//一个小时
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public long getTtl() {
return ttl;
}
public void setTtl(long ttl) {
this.ttl = ttl;
}
/**
* 生成JWT
*
* @param id 当前登录用户的id
* @param subject 登录用户名
* @param roles admin user 角色
*
*/
public String createJWT(String id, String subject, String roles) {
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
JwtBuilder builder = Jwts.builder().setId(id)
.setSubject(subject)
.setIssuedAt(now)
//注意,这里的claim相当于自定义键值对
.signWith(SignatureAlgorithm.HS256, key).claim("roles", roles);
if (ttl > 0) {
//设置过期时间,也可以不设置。注意,如果解析时过期了会报错,最好try起来
builder.setExpiration( new Date( nowMillis + ttl));
}
return builder.compact();
}
/**
* 解析JWT,如果过期了会报错,最好try起来
* 解析出来的Claims对象它的结构跟上面加密的一样,里面有很多键值对,需要的话
* 可以取出来
*
* @param jwtStr
* @return
*/
public Claims parseJWT(String jwtStr){
return Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(jwtStr)
.getBody();
}
}

浙公网安备 33010602011771号