实现基于JWT的Token登陆验证功能

前言

放假以前作了几个小项目+课设,都用到了token实现登陆验证和权限判断,然鹅当时和同组的小伙伴也都是第一次接触到了token,因而乎都是一脸懵逼(xjbx)的写完了登陆验证的先后端逻辑(我写前端,同组的小伙伴写后端)。今天有空仔细学习了一下SpringBoot实现token认证以及和前端的交互,踩了很多坑,在这里记录一下前端

后端实现

  • 首先须要导入jwt的包,相关的pom.xml文件以下:
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.5.0</version>
</dependency>
复制代码
  • 而后开始编写TokenUtil类,首先定义token的过时时间和私钥
private static final long EXPIRE_TIME = 15 * 60 * 1000;
private static final String TOKEN_SECRET = "thefirsttoken123";
复制代码
  • 实现签名方法: 这里不该该使用密码进行加密,不安全,可是是本身的小demo就这样写了。
/**
 * 生成签名,15分钟过时
 * @param **username**
* @param **password**
* @return
 */
public static String sign(String username, String password) {
    try {
        // 设置过时时间
        Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
        // 私钥和加密算法
        Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
        // 设置头部信息
        Map<String, Object> header = new HashMap<>(2);
        header.put("Type", "Jwt");
        header.put("alg", "HS256");
        // 返回token字符串
        return JWT.create()
                .withHeader(header)
                .withClaim("loginName", username)
                .withClaim("pwd", password)
                .withExpiresAt(date)
                .sign(algorithm);
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}
复制代码
  • 实现token的检验方法:
/**
 * 检验token是否正确
 * @param **token**
* @return
 */
public static boolean verify(String token){
    try {
        Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
        JWTVerifier verifier = JWT.require(algorithm).build();
        DecodedJWT jwt = verifier.verify(token);
        return true;
    } catch (Exception e){
        return false;
    }
}
复制代码

至此,工具类就编写完成啦!vue

  • 登陆的controller层方法 这里获取到前端发送过来的请求体,取出其中的用户名和密码,和数据库比对若是无误的话,签发token,并返回给前端。 (API响应结果尚未封装,看着有点乱,嘿嘿)
@PostMapping(value = "/login")
public Map<String, Object> login(@RequestBody SysUser sysUser){
    Map<String, Object> map = new HashMap<>();
    String username = sysUser.getUsername();
    String password = sysUser.getPassword();
    if (sysUserService.login(username, password)){
        String token = TokenUtil.sign(username,password);
        if (token != null){
            map.put("code", "10000");
            map.put("message","认证成功");
            map.put("token", token);
            return map;
        }
    }
    map.put("code", "00000");
    map.put("message","认证失败");
    return map;
}
复制代码

如今服务端给客户端签发token的功能已经差很少实现了。 那么客户端如何将token应用到之后的请求中,服务端又如何识别token呢?java

  • 实现服务端自定义拦截器
/**
 * 自定义token拦截器
 */
@Component
public class TokenInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        if (request.getMethod().equals("OPTIONS")){
            response.setStatus(HttpServletResponse.SC_OK);
            return true;
        }
        response.setCharacterEncoding("utf-8");
        String token = request.getHeader("admin-token");
        if (token != null){
            boolean result = TokenUtil.verify(token);
            if(result){
                System.out.println("经过拦截器");
                return true;
            }
        }
        System.out.println("认证失败");
        response.getWriter().write("50000");
        return false;
    }
}
复制代码

TokenInterceptor实现了HandlerInterceptor接口,重写了preHandle方法,该方法是在每一个请求以前触发执行,从request的头里面取出token,这里咱们统一了存放token的键为admin-token,验证经过,放行,验证不经过,返回认证失败信息。 这里有一个坑,因为使用axios,每次前端发送请求,都会先发一次预请求,也就是RequestMethod为OPTIONS不是咱们常见的get、post等(关于OPTIONS的解释,能够谷歌一下)。全部在这里咱们须要作一次判断,若是请求方法为OPTIONS,就直接return经过。ios

  • 配置拦截器 对登陆界面的请求不拦截
@Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
    private TokenInterceptor tokenInterceptor;

    public InterceptorConfig(TokenInterceptor tokenInterceptor) {
        this.tokenInterceptor = tokenInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        List<String> excludePath = new ArrayList<>();
        String sysUserLogin = "/api/sysUser/login";
        excludePath.add(sysUserLogin);
        registry.addInterceptor(tokenInterceptor).excludePathPatterns(excludePath);
    }
}
复制代码
  • 服务端解析token 如今为了以后根据token去作相关的查询,咱们须要对token进行解密,取出以前加密的loginName。而后就能够愉快的增删查改啦~
/**
 * 从token中获取username信息
 * @param **token**
* @return
 */
public static String getUserName(String token){
    try {
        DecodedJWT jwt = JWT.decode(token);
        return jwt.getClaim("loginName").asString();
    } catch (JWTDecodeException e){
        e.printStackTrace();
        return null;
    }
}
复制代码

前端实现

前端使用vue+axios,主要是实现对axios的再封装。 相关代码以下 大体逻辑就是,若是vuex中已经存在了token,那么就把它放到请求头中发往服务端。算法

// 建立axios实例
const service = axios.create({
  baseURL: process.env.BASE_API // api 的 base_url
})

// request拦截器
service.interceptors.request.use(
  config => {
    if (store.getters.token) {
      config.headers['admin-token'] = getToken() // 让每一个请求携带自定义token
    }
    return config
  },
  error => {
    // 出错
    console.log(error)
    Promise.reject(error)
  }
)
复制代码

结语

哇在掘金的第一篇文章写完了,嘻嘻,继续努力!!!。vuex

昨日今日明日日日码不停蹄,
去年今年明年年年都是单身。
横批:这就是命数据库

相关文章
相关标签/搜索