Java实现JWT的Token认证机制

本文介绍了如何使用io.jsonwebtoken库在Java中创建JWT令牌,包括设置签发时间、签名、过期时间以及自定义claims。还展示了如何在Spring框架中使用JWT进行登录鉴权和拦截器实现全局验证。
摘要由CSDN通过智能技术生成

io.jsonwebtoken

jjwt

0.6.0

(2)创建类CreatejwtTest,用于生成token

public class CreateJwtTest {

public static void main(String[] args) {

JwtBuilder builder= Jwts.builder().setId(“888”)

.setSubject(“小白”)

.setIssuedAt(new Date())//用于设置签发时间

.signWith(SignatureAlgorithm.HS256,“wangmh”);//用于设置签名秘钥

System.out.println( builder.compact() );

}

}

(3)测试

eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1MjM0M

TM0NTh9.gq0J‐cOM_qCNqU_s‐d_IrRytaNenesPmqAIhQpYXHZk

#再次运行,每次运行结果都会不一样,因为我们载荷中包含了时间

3.token的解析

public class ParseJwtTest {

public static void main(String[] args) {

String compactJws=“eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1MjM0MTM0NTh9.gq0J‐cOM_qCNqU_s‐d_IrRytaNenesPmqAIhQpYXHZk”;

Claims claims =

Jwts.parser().setSigningKey(“wangmh”).parseClaimsJws(compactJws).getBody();

System.out.println(“id:”+claims.getId());

System.out.println(“subject:”+claims.getSubject());

System.out.println(“IssuedAt:”+claims.getIssuedAt());

}

}

//试着将token或签名秘钥篡改一下,会发现运行时就会报错,所以解析token也就是验证token

4.token过期校验

有很多时候,我们并不希望签发的token是永久生效的,所以我们可以为token添加一个

过期时间。

public class CreateJwtTest2 {

public static void main(String[] args) {

//为了方便测试,我们将过期时间设置为1分钟

long now = System.currentTimeMillis();//当前时间

long exp = now + 1000*60;//过期时间为1分钟

JwtBuilder builder= Jwts.builder().setId(“888”)

.setSubject(“小白”)

.setIssuedAt(new Date())

.signWith(SignatureAlgorithm.HS256,“wangmh”)

.setExpiration(new Date(exp));//用于设置过期时间

System.out.println( builder.compact() );

}

}

public class ParseJwtTest {

public static void main(String[] args) {

String compactJws=“eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1MjM0MTY1NjksImV4cCI6MTUyMzQxNjYyOX0.Tk91b6mvyjpKcldkic8DgXz0zsPFFnRgTgkgcAsa9cc”;

Claims claims =Jwts.parser().setSigningKey(“wangmh”).parseClaimsJws(compactJws).getBody();

System.out.println(“id:”+claims.getId());

System.out.println(“subject:”+claims.getSubject());

SimpleDateFormat sdf=new SimpleDateFormat(“yyyy‐MM‐dd hh:mm:ss”);

System.out.println(“签发时间:”+sdf.format(claims.getIssuedAt()));

System.out.println(“过期时间:”+sdf.format(claims.getExpiration()));

System.out.println(“当前时间:”+sdf.format(new Date()) );

}

}

//测试运行,当未过期时可以正常读取,当过期时会引发io.jsonwebtoken.ExpiredJwtException异常。

5.自定义claims

我们刚才的例子只是存储了id和subject两个信息,如果你想存储更多的信息(例如角色)可以定义自定义claims

public class CreateJwtTest3 {

public static void main(String[] args) {

//为了方便测试,我们将过期时间设置为1分钟

long now = System.currentTimeMillis();//当前时间

long exp = now + 1000*60;//过期时间为1分钟

JwtBuilder builder= Jwts.builder().setId(“888”)

.setSubject(“小白”)

.setIssuedAt(new Date())

.signWith(SignatureAlgorithm.HS256,“wangmh”)

.setExpiration(new Date(exp))

.claim(“roles”,“admin”)

.claim(“logo”,“logo.png”);

System.out.println( builder.compact() );

String compactJwt = “eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NTQ5NDcxNTksImV4cCI6MTU1NDk0NzIxOSwicm9sZXMiOiJhZG1pbiIsImxvZ28iOiJsb2dvLnBuZyJ9.HD_myvdNjOGGnu4p8Z8QX9dnXHJEZa0nsLKxOFRiYJY”;

Claims claims=Jwts.parser().setSigningKey(“wangmh”).parseClaimsJws(compactJwt).getBody();

System.out.println(“id:”+claims.getId());

System.out.println(“subject:”+claims.getSubject());

System.out.println(“roles:”+claims.get(“roles”));

System.out.println(“logo:”+claims.get(“logo”));

SimpleDateFormat sdf=new SimpleDateFormat(“yyyy‐MM‐dd hh:mm:ss”);

System.out.println(“签发时间:”+sdf.format(claims.getIssuedAt()));

System.out.println(“过期时间:”+sdf.format(claims.getExpiration()));

System.out.println(“当前时间:”+sdf.format(new Date()) );

}

}

6.案例实现

//JwtUtil.java

@ConfigurationProperties(“jwt.config”)

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

  • @param subject

  • @return

*/

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)

.signWith(SignatureAlgorithm.HS256, key).claim(“roles”, roles);

if (ttl > 0) {

builder.setExpiration( new Date( nowMillis + ttl));

}

return builder.compact();

}

/**

  • 解析JWT

  • @param jwtStr

  • @return

*/

public Claims parseJWT(String jwtStr){

return Jwts.parser()

.setSigningKey(key)

.parseClaimsJws(jwtStr)

.getBody();

}

}

#配置文件

jwt:

config:

key: wangmh

ttl: 3600000

登录鉴权

//Controller.java

@Autowired

private JwtUtil jwtUtil;

@RequestMapping(value=“/login”,method=RequestMethod.POST)

public Result login(@RequestBody Map<String,String> loginMap){

Admin admin =adminService.findByLoginnameAndPassword(loginMap.get(“loginname”),loginMap.get(“password”));

if(admin!=null){

//生成token

String token = jwtUtil.createJWT(admin.getId(),admin.getLoginname(), “admin”);

Map map=new HashMap();

map.put(“token”,token);

map.put(“name”,admin.getLoginname());//登陆名

return new Result(true,StatusCode.OK,“登陆成功”,map);

}else{

return new Result(false,StatusCode.LOGINERROR,“用户名或密码错误”);

}

}

删除用户功能鉴权

@Autowired

private HttpServletRequest request;

/**

  • 删除

  • @param id

*/

@RequestMapping(value=“/{id}”,method= RequestMethod.DELETE)

public Result delete(@PathVariable String id ){

String authHeader = request.getHeader(“Authorization”);//获取头信息

if(authHeader==null){

return new Result(false,StatusCode.ACCESSERROR,“权限不足”);

}

if(!authHeader.startsWith("Bearer ")){

return new Result(false,StatusCode.ACCESSERROR,“权限不足”);

}

String token=authHeader.substring(7);//提取token

Claims claims = jwtUtil.parseJWT(token);

if(claims==null){

return new Result(false,StatusCode.ACCESSERROR,“权限不足”);

}

if(!“admin”.equals(claims.get(“roles”))){

return new Result(false,StatusCode.ACCESSERROR,“权限不足”);

}

userService.deleteById(id);

return new Result(true,StatusCode.OK,“删除成功”);

}

使用拦截器方式实现token鉴权

如果我们每个方法都去写一段代码,冗余度太高,不利于维护。因此我们可以使用拦截器的方式去实现token鉴权

1.添加拦截器

Spring为我们提供了org.springframework.web.servlet.handler.HandlerInterceptorAdapter这个适配器,继承此类,可以非常方便的实现自己的拦截器。他有三个方法:分别实现预处理、后处理(调用了Service并返回ModelAndView,但未进行页面渲染)、返回处理(已经渲染了页面)

在preHandle中,可以进行编码、安全控制等处理;

在postHandle中,有机会修改ModelAndView;

在afterCompletion中,可以根据ex是否为null判断是否发生了异常,进行日志记录。

1.1创建拦截器类

//JwtFilter.java

@Component

public class JwtFilter extends HandlerInterceptorAdapter {

@Autowired

private JwtUtil jwtUtil;

@Override

public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler)throws Exception {

System.out.println(“经过了拦截器”);

final String authHeader = request.getHeader(“Authorization”);

if (authHeader != null && authHeader.startsWith("Bearer ")) {

final String token = authHeader.substring(7); // The partafter "Bearer "

Claims claims = jwtUtil.parseJWT(token);

if (claims != null) {

if(“admin”.equals(claims.get(“roles”))){//如果是管理员

request.setAttribute(“admin_claims”, claims);

}

if(“user”.equals(claims.get(“roles”))){//如果是用户

request.setAttribute(“user_claims”, claims);

}

}

}

return true;

}

}

1.2配置拦截器类

@Configuration

public class ApplicationConfig extends WebMvcConfigurationSupport {

@Autowired

private JwtFilter jwtFilter;

@Override

public void addInterceptors(InterceptorRegistry registry) {

registry.addInterceptor(jwtFilter).

addPathPatterns(“/**”).

excludePathPatterns(“/**/login”);

}

}

1.3删除功能实现

/**

  • 删除

  • @param id

*/
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

总而言之,面试官问来问去,问的那些Redis知识点也就这么多吧,复习的不够到位,知识点掌握不够熟练,所以面试才会卡壳。将这些Redis面试知识解析以及我整理的一些学习笔记分享出来给大家参考学习

还有更多学习笔记面试资料也分享如下:

都是“Redis惹的祸”,害我差点挂在美团三面,真是“虚惊一场”

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
9eJY-1713294016393)]

[外链图片转存中…(img-OxSO4gx7-1713294016393)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

总而言之,面试官问来问去,问的那些Redis知识点也就这么多吧,复习的不够到位,知识点掌握不够熟练,所以面试才会卡壳。将这些Redis面试知识解析以及我整理的一些学习笔记分享出来给大家参考学习

还有更多学习笔记面试资料也分享如下:

[外链图片转存中…(img-agoBPYvn-1713294016394)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 28
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值