Bootstrap

Vue+Spring Boot实现token认证

前言
在使用 Vue开发前后分离的项目以前,后台可以通过 Session 储存和获取当前登录的用户信息,进行权限管理。但使用Vue之后,发现虽然前端浏览器没有打开过新的页面。但后台每一次获取请求的Session都不相同。后台无法通过Session辨别当前用户。经过查找资料才发现,如今可以通过token进行身份验证
Vue+Spring Boot实现token认证主要可分为四步
  1. 后台设置拦截器拦截请求,判断请求是否携带token或者判断携带token的权限
  2. 用户登录成功后,后台根据用户信息生成token,并将token的信息封入response
  3. vue收到token后,存入localStorage
  4. axios发送请求时,将token的内容封装到请求

后台配置

创建注解类

package com.booksystem.token;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

maven引入jwt

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

配置拦截器

package com.booksystem.token;

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.booksystem.pojo.User;
import com.booksystem.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

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

public class Authentication 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();
        //检查有没有需要用户权限的注解
        if (method.isAnnotationPresent(UserToken.class)) {
            UserToken userToken = method.getAnnotation(UserToken.class);
            if (userToken.required()) {
                // 执行认证
                if (token == null) {
                    throw new RuntimeException("无token,请重新登录");
                }
                // 获取 token 中的 user id
                String userId = null;

                try {
                    userId = JWT.decode(token).getAudience().get(0);
                } catch (JWTDecodeException j) {
                    throw new RuntimeException("401");
                }
                User user = userService.findUserById(Integer.parseInt(userId));
                if (user == null) {
                    throw new RuntimeException("用户不存在,请重新登录");
                }
                // 验证 token
                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
                try {
                    jwtVerifier.verify(token);
                } catch (JWTVerificationException e) {
                    throw new RuntimeException("401");
                }
                return true;
            }
        }
        return true;
    }
}

配置configuration

package com.booksystem.config;

import com.booksystem.token.Authentication;
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(authentication())
                .addPathPatterns("/**");
    }
    @Bean
    public Authentication authentication() {
        return new Authentication();
    }
}

配置TokenService

package com.booksystem.service;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.booksystem.pojo.User;
import org.springframework.stereotype.Service;

import java.util.Date;

@Service
public class TokenService {

    public String getToken(User user) {
        Date start = new Date();
        long currentTime = System.currentTimeMillis() + 60* 60 * 1000;//一小时有效时间
        Date end = new Date(currentTime);
        String token = "";

        token = JWT.create().withAudience(String.valueOf(user.getId())).withIssuedAt(start).withExpiresAt(end)
                .sign(Algorithm.HMAC256(user.getPassword()));
        return token;
    }
}

UserController

package com.booksystem.controller;

import com.alibaba.fastjson.JSONObject;

import com.booksystem.pojo.User;
import com.booksystem.service.UserService;
import com.booksystem.service.impl.TokenService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@Controller
public class LoginController {

    @Autowired
    UserService userService;
    @Autowired
    TokenService tokenService;

    @ResponseBody
    @GetMapping("/login")
    public Object login(User user){
        User u = userService.userLogin(user);
        JSONObject jsonObject = new JSONObject();
        if (u != null){
            String token = tokenService.getToken(u);
            jsonObject.put("token",token);
            jsonObject.put("user",u);
	        jsonObject.put("msg","登录成功");
            jsonObject.put("code",200);
        }else{
        	jsonObject.put("msg","账号或密码错误");
			jsonObject.put("code",500);
		}
        return jsonObject;
    }

	//测试方法
    @GetMapping("/getMessage")
    @ResponseBody
    @UserToken
    public String getMessage(){
        System.out.println("访问成功");
        return "你已通过验证";
    }
}

前端配置

在 vue 的 src 目录下 main.js 配置 axios 。若 token 存在,axios 的每一次请求都会携带上 token

//将token添加到axios请求头部
axios.interceptors.request.use(
    config => {
        if (localStorage.getItem('token')) {
            config.headers.token = localStorage.getItem('token')
        }
        return config
    },
    error => {
        return Promise.reject(error)
    }
)

配置Vuex

const store = new Vuex.Store({
    state: {
        user: localStorage.getItem('user') ? localStorage.getItem('user') : null,
        //若localSorage存在token,将值赋给Vuex.state.token
        token: localStorage.getItem('token') ? localStorage.getItem('token') : null,
    },
    mutations: {
        setUser(state, user) {
            state.user = user
            localStorage.setItem('user', JSON.stringify(user))
        },
        setToken(state, token) {
            localStorage.setItem('token', token)
            state.token = token
        },
        logout(state) {
            localStorage.removeItem('token')
            state.token = null
            localStorage.removeItem('user')
            state.user = null
        }
    }
})

前端的登录方法

login(){
   var vm = this
   vm.axios.get('http://localhost:8080/login',{
     params:{
       username:this.username,
       password:this.password
     }
   }).then(function (response) {
     if(response.data.code == 200){
       console.log(response.data.msg)
       //将token和user保存到localStorage中
       vm.$store.commit('setToken',response.data.token)
       vm.$store.commit('setUser',response.data.user)
       //跳转到登录成功后的页面
       vm.$router.push({path:'/index'})
	 else{
       alert(response.data.msg)
     }
   }).catch(function (e) {
     console.log(e)
   })
 }

根据需要,也可以配置router拦截器。若localStorage中无token,则只能访问登录和注册页面

//配置路由拦截器
router.beforeEach((to, from, next) => {
    if (to.path === '/login' || to.path === '/register') {
        next()
    } else {
        const token = window.localStorage.getItem('token')
        if (!token) {
            return next('/login')
        }
        next()
    }
})


经过以上配置后,就能实现token身份认证。
注意:以上配置是在原来的基础上进行的改动,可能需要一定的 Vue 和 spring boot 基础。
参考博客:https://www.cnblogs.com/zwq20134/p/11687820.html

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;