项目实训6

项目实训6

1. token的作用

为什么使用Token验证:

所谓的Token,其实就是服务端生成的一串加密字符串、以作客户端进行请求的一个“令牌”。当用户第一次使用账号密码成功进行登录后,服务器便生成一个Token及Token失效时间并将此返回给客户端,若成功登陆,以后客户端只需在有效时间内带上这个Token前来请求数据即可,无需再次带上用户名和密码。

Token就为我们免去了每次打开应用都要输入账号跟密码的过程。

为什么要使用Token?

为什么要使用Token?这个问题其实很好回答——因为它能解决问题!

当下用户对产品的使用体验要求在逐渐提高,从产品体验方面来讲,Token带来的体验更容易能让用户接受。

那么Token都可以解决哪些问题呢?

  • Token具有随机性、不可预测性、时效性、无状态、跨域等特点。
  • Token完全由应用管理,所以它可以避开同源策略
  • Token可以避免CSRF攻击
  • Token可以是无状态的,可以在多个服务间共享
  • Token是在服务端产生的。如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回Token给前端。前端可以在每次请求的时候带上Token证明自己的合法地位。如果这个Token在服务端持久化(比如存入数据库),那它就是一个永久的身份令牌。

2. 项目中token的使用

引入JWT依赖,由于是基于Java,所以需要的是java-jwt

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

2.1 jwt的结构

JWT是由三段信息构成的,将这三段信息文本用.连接一起就构成了JWT字符串。
就像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

JWT包含了三部分:
Header 头部(标题包含了令牌的元数据,并且包含签名和/或加密算法的类型)
Payload 负载 (类似于飞机上承载的物品)
Signature 签名/签证

JWT的头部承载两部分信息:token类型和采用的加密算法。

载荷就是存放有效信息的地方。
有效信息包含三个部分
1.标准中注册的声明
2.公共的声明
3.私有的声明

jwt的第三部分是一个签证信息
这个部分需要base64加密后的headerbase64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。**

  1. 生成token工具类
package com.example.guke.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.example.guke.entity.User;
import com.example.guke.service.UserService;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class JwtUitls {

    @Autowired
    private UserService userService;

    /**
     * 生成token
     * @param user  用户
     * @return token 签名
     */
    public String getToken(User user) {
        String token="";
        token= JWT.create().withAudience(user.getOpenid())
                .sign(Algorithm.HMAC256(user.getNickname()));
        return token;
    }

}

  1. 定义拦截器
package com.example.guke.interceptor;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.example.guke.annotation.PassToken;
import com.example.guke.annotation.UserLoginToken;
import com.example.guke.entity.User;
import com.example.guke.exception.CustomException;
import com.example.guke.result.ResultCodeEnum;
import com.example.guke.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

@Slf4j
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");// 从 http 请求头中取出 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;
            }
        }
        //检查有没有需要用户权限的注解
        if (method.isAnnotationPresent(UserLoginToken.class)) {
            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
            if (userLoginToken.required()) {
                // 执行认证
                if (token == null) {
                    throw new CustomException(ResultCodeEnum.NO_TOKEN);
                }
                // 获取 token 中的 user openid
                String openid;
                try {
                    openid = JWT.decode(token).getAudience().get(0);
//                    log.info(openid);
                } catch (JWTDecodeException j) {
                    throw new RuntimeException("token解析出错,请联系管理员");
                }
                User user = userService.findUserByOpenid(openid);
//                log.info(user.toString());
                if (user == null) {
                    throw new CustomException(ResultCodeEnum.UNKNOWN_USER);
                }
                // 验证 token
                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getNickname())).build();
                try {
                    jwtVerifier.verify(token);
                } catch (JWTVerificationException e) {
                    throw new RuntimeException("token错误");
                }
                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 {
    }
}

主要目的是在需要的时候判断请求头中是否有token字段,如果没有可以使用之前所提到的统一异常处理直接返回相应的异常。

HandlerInterceptor接口主要定义了三个方法
1.boolean preHandle ()
预处理回调方法,实现处理器的预处理,第三个参数为响应的处理器,自定义Controller,返回值为true表示继续流程(如调用下一个拦截器或处理器)或者接着执行
postHandle()afterCompletion()false表示流程中断,不会继续调用其他的拦截器或处理器,中断执行。

2.void postHandle()
后处理回调方法,实现处理器的后处理(DispatcherServlet进行视图返回渲染之前进行调用),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null

3.void afterCompletion():
整个请求处理完毕回调方法,该方法也是需要当前对应的InterceptorpreHandle()的返回值为true时才会执行,也就是在DispatcherServlet渲染了对应的视图之后执行。用于进行资源清理。整个请求处理完毕回调方法。如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中

  1. 定义拦截器规则
package com.example.guke.interceptor;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticationInterceptor())
                .addPathPatterns("/**");
    }
    @Bean
    public AuthenticationInterceptor authenticationInterceptor() {
        return new AuthenticationInterceptor();
    }
}

  1. 整个的主要流程

1.从 http 请求头中取出 token
2.判断是否映射到方法
3.检查是否有passtoken注释,有则跳过认证
4.检查有没有需要用户登录的注解,有则需要取出并验证
5.认证通过则可以访问,不通过会报相关错误信息

3. 自定义注解

通过自定义的注解在需要的地方判断token的存在,自定义注解见博客中其他的文章。

所以,我们便完成了项目中的token的使用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值