Springboot--整合jwt实现token令牌认证

JWT请求的流程:
1.用户输入账号密码登录,前端传输给后端认证
2.认证成功后,后端用jwt创建一个token令牌
3.后端传给前端token令牌,前端把这个令牌保存下来
4.前端每次访问后端资源的时候都要带上之前保存的token令牌给后端认证
5.验证成功后返回资源给前端


JWT组成
JWT有3部分组成:
Header(头部):两部分组成,令牌的元数据,签名和/或加密算法的类型
Payload(载体):JWT中的一些数据比如签发者、过期时间,签发时间之类
Signature(签名):由base64对header部分编码后加上.和base64对Payload编码后组成的字符串,再用一个密钥sercet对组合成的字符串用由头部声明的加密方式加密组成
一个JWT如下:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxIiwiZXhwIjoxNTc4MzA0NzI2LCJpYXQiOjE1NzgzMDExMjZ9.Vvh49ThZlFNsoTVj09QA4Wb51s7-Jk1-LyQHPuVpZCA


Springboot中整合JWT
先在 pom.xml 中引入jar包

        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>

然后创建两个注解 PassToken(有此注解的接口不需要验证Token)和UserLoginToken(有此注解的接口需要验证Token)

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
    boolean required() default true;
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
    boolean required() default true;
}

创建一个token工具类,我在后面的token创建中Payload中只放了用户的ID,TOKEN创建时间,有效时间。所以在这个工具类中写了个获取用户ID的方法。
TokenUtil.java

public class TokenUtil {
	//从token中拿到用户的ID
    public static String getTokenUserId() {
        String token = getRequest().getHeader("token");// 从 http 请求头中取出 token
        String userId = JWT.decode(token).getAudience().get(0);
        return userId;
    }

    /**
     * 获取request
     *
     * @return
     */
    public static HttpServletRequest getRequest() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        return requestAttributes == null ? null : requestAttributes.getRequest();
    }
}

创建一个TokenService 来创建Token,也就是颁发Token。
TokenService.java

	@Service
public class TokenService {
	//颁发Token
    public String getToken(User user){
        Date start = new Date();
        long current=System.currentTimeMillis()+60*60*1000;     //1小时有效期
        Date end=new Date(current);

        String token="";
        
        token= JWT.create().withAudience(user.getUserid()).withIssuedAt(start).withExpiresAt(end)
                //这里用了HMAC加密,密钥是用户的密码
                .sign(Algorithm.HMAC256(user.getPassword()));
        return token;
    }
}

然后创建一个拦截器,这个拦截器拦截所有请求,然后判断请求的接口有没有上面自定义的注解@UserLoginToken 注解,有该注解表示需要Token,若是遇到@PassToken 注解 则表示不需要Token,直接放行。
AuthenticationInterceptor.java

public class AuthenticationInterceptor implements HandlerInterceptor {
    @Autowired
    UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        String token = httpServletRequest.getHeader("token");

        if (!(object instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();
        //判断是否有注解PassToken
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passtoken = method.getAnnotation(PassToken.class);
            if (passtoken.required())
                return true;
        }
		//判断是否有注解UserLoginToken
        if (method.isAnnotationPresent(UserLoginToken.class)) {
            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
			
            if (userLoginToken.required()) {
                if (token == null)
                    throw new RuntimeException("无token,请重新登录");
                String userid;
                try {
                    userid = JWT.decode(token).getAudience().get(0);
                    boolean time= JWT.decode(token).getExpiresAt().before(new Date());
                } catch (JWTDecodeException j) {
                    throw new RuntimeException("401");
                }
                //此处通过用户ID获得用户
                User user = userService.selectByPrimaryKey(userid);
                if (user == null)
                    throw new RuntimeException("用户不存在,请重新登录");
                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
                try {
                    jwtVerifier.verify(token);
                } catch (Exception j) {
                    throw new RuntimeException("401");
                }
                return true;
            }
        }
        return true;
    }

    @Override
        public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
    }
}

最后需要把我们刚才创建的拦截器进行注册。
InterceptorConfig.java

@Configuration
public class InterceptorConfig  implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticationInterceptor())
                .addPathPatterns("/**");    // 拦截所有请求,通过判断是否有 @UserLoginToken注解 决定是否需要登录
    }
    @Bean
    public AuthenticationInterceptor authenticationInterceptor() {
        return new AuthenticationInterceptor();// 自己写的拦截器
    }
}

至此,Springboot整合jwt实现token的过程完成。
接下来要进行测试,创建一个测试用的User Bean
User.java

public class User {

    private String userid;


    private String account;


    private String password;
	//省略get和set
}

controller层创建测试api
TestController.java

@Controller
public class TestController {
    @Autowired
    TokenService tokenService;

    @RequestMapping(value = "/login/{account}/{password}" ,method = RequestMethod.GET)
    @ResponseBody
    public String login(HttpServletResponse response, @PathVariable String account, @PathVariable String password){
        User userForBase = new User();
        userForBase.setUserid("1");
        userForBase.setPassword("123");
        userForBase.setAccount("qwe");

        if (!userForBase.getPassword().equals(password)) {
            return "登录失败,密码错误";
        } else {
            String token = tokenService.getToken(userForBase);
            //创建一个名为token的cookie,供前端调用
            Cookie cookie = new Cookie("token", token);
            cookie.setPath("/");
            response.addCookie(cookie);
            return token;
        }
    }

    @UserLoginToken
    @RequestMapping("/verify")
    @ResponseBody
    public String verify() {
        // 取出token中带的用户id 进行操作
        System.out.println(TokenUtil.getTokenUserId());
        return "你已通过验证";
    }
}

使用postmen来测试
在这里插入图片描述
下面返回的内容就是Token
这是返回的cookie
在这里插入图片描述
然后请求 localhost:8080/verify 接口 带上前面返回的token参数
在这里插入图片描述
在这里插入图片描述
如上图 表示带上的token验证通过,且在控制台输出了用户的ID

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值