SpringBoot + Vue集成JWT实现登录认证

本文说明SpringBoot + Vue集成JWT实现登录认证

1.后端(SpringBoot)

1.1 pom.xml 文件


<dependency>
    <groupId>com.auth0groupId>
    <artifactId>java-jwtartifactId>
    <version>3.10.3version>
dependency>

<dependency>
    <groupId>net.sf.json-libgroupId>
    <artifactId>json-libartifactId>
    <version>2.4version>
    <classifier>jdk15classifier>
dependency>

1.2 TokenUtil.java (工具类,用于生成Token和验证Token)

/**
 * @author hanmingzhi
 * @date 2022/8/14 18:47
 **/
public class TokenUtil {
    //token到期时间30分钟(根据需求改)
    private static final long EXPIRE_TIME= 30*60*1000;
    //密钥 (随机生成,可以从网上找到随机密钥生成器)
    private static final String TOKEN_SECRET="MD9**+4MG^EG79RV+T?J87AI4NWQVT^&";
    /** 生成token */
    public static String createToken(User user){
        String token=null;
        try {
            Date expireAt=new Date(System.currentTimeMillis()+EXPIRE_TIME);
            token = JWT.create()
                    //发行人
                    .withIssuer("auth0")
                    //存放数据
                    .withClaim("email",user.getEmail())
                    .withClaim("id",user.getId())
                    //过期时间
                    .withExpiresAt(expireAt)
                    .sign(Algorithm.HMAC256(TOKEN_SECRET));
        } catch (IllegalArgumentException| JWTCreationException je) {}
        return token;
    }
    /** token验证 */
    public static Boolean checkToken(String token){
        try {
            //创建token验证器
            JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).withIssuer("auth0").build();
            DecodedJWT decodedJWT=jwtVerifier.verify(token);
            System.out.println("认证通过:");
            System.out.println("id: " + decodedJWT.getClaim("id").asInt());
            System.out.println("email: " + decodedJWT.getClaim("email").asString());
            System.out.println("过期时间:" + decodedJWT.getExpiresAt());
        } catch (IllegalArgumentException | JWTVerificationException e) {
            //抛出错误即为验证不通过
            return false;
        }
        return true;
    }
}

1.3 TokenInterceptor.java (拦截器,用来请求时的拦截,验证token)

/**
 * @author hanmingzhi
 * @date 2022/8/14 18:46
 **/

@Component
public class TokenInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //跨域请求会首先发一个option请求,直接返回正常状态并通过拦截器
        if(request.getMethod().equals("OPTIONS")){
            response.setStatus(HttpServletResponse.SC_OK);
            return true;
        }
        String token = request.getHeader("token");
        String requestUrl = request.getServletPath();
        if (token!=null){
            boolean result= TokenUtil.checkToken(token); // 验证token
            if (result){
                System.out.println("通过拦截器:"+requestUrl);  // 可以输出接口是否通过拦截器
                return true;
            }
        }
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json; charset=utf-8");
        try {
            JSONObject json=new JSONObject();
            json.put("msg","token认证失败");
            json.put("code","500");
            response.getWriter().append(json.toString());
            System.out.println("认证失败,未通过拦截器:"+requestUrl);
        } catch (Exception e) {
            return false;
        }
        /**
         * 还可以在此处检验其他操作
         */
        return false;
    }
}

1.4 MyWebMvcConfig.java (配置类,用来配置跨域请求和注册拦截器)

@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private TokenInterceptor tokenInterceptor;
// 解决跨域问题
    @Override 
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedHeaders("*")
                .allowedMethods("*")
                .maxAge(1800)
                .allowedOrigins("*");
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        List<String> excludePath = new ArrayList<>();
        //这里用来排除拦截的接口,例如登录前调用的接口
        excludePath.add("/userLogin");  //登录
        excludePath.add("/userRegister");     //注册
        excludePath.add("/static/**");  //静态资源
        excludePath.add("/checkToken"); // 检测token是否正确
        excludePath.add("/findEmail"); // 查询邮箱是否注册
        excludePath.add("/getEmailCode"); //获取邮箱验证码
        registry.addInterceptor(tokenInterceptor) // 注册拦截器
                .addPathPatterns("/**")
                .excludePathPatterns(excludePath);
        WebMvcConfigurer.super.addInterceptors(registry);

    }
}

1.5 UserController.java (控制类,用来接收前端发来的请求,返回token)

/**
 * @author hanmingzhi
 * @date 2022/8/13 19:29
 **/
@RestController
public class UserController {

    @Autowired
    UserMapper userMapper ;
    // 用户登录
    @PostMapping("userLogin")
    public JSONObject userLogin(String email, String password){
        User user = userMapper.getUserByEmail(email);
        String token = "" ;
        String msg="";
        JSONObject res = new JSONObject();
        if(user != null){
            if(user.getPassword().equals(password)){
                msg="登录成功,正在进入首页。。。";
                token= TokenUtil.createToken(user);  // 登录成功后,创建一个token
                res.put("user",user);
            }else{
                msg="登录失败,请检查密码";
            }
        }
        res.put("token",token);
        res.put("msg",msg);
        return res;
    }
        // 检验token
    @GetMapping("/checkToken")
    public Boolean checkToken(HttpServletRequest request){
        System.out.println("前端访问页面验证token");
        String token = request.getHeader("token");
        return TokenUtil.checkToken(token);
    }
}

2.前端(vue)

2.1 login.vue (登录成功后,把用户信息和token保存起来)

// 登录
onLogin() {
    console.log("进入登录")
    let _this = this
    axios({
        method: 'post',
        url: 'http://localhost:8210/userLogin',
        params: {
            email: this.formObj.email,
            password: this.formObj.password
        },
        header: {
            'content-type': 'application/x-www-form-urlencoded',
        },
    }).then(function (response) {
        // 登录成功,保存token 和用户信息
        if (response.data.token != '') {
            ElMessage({ message: response.data.msg,type: 'success',})
            localStorage.setItem("token", response.data.token)
            localStorage.setItem("user", JSON.stringify(response.data.user))
            _this.$store.commit('saveUser', response.data.user)
            _this.$store.commit('saveToken', response.data.token)
            setTimeout(() => {
                _this.$router.replace({ path: '/index' })
            }, 2000);
        }else{
            ElMessage({ message: response.data.msg,type: 'error',})
            return
        }
    });
},

2.2 router.js (配置路由规则,检验当前token是否可用)

//路由设置
router.beforeEach((to, from, next) => {
  if (to.path === '/') { // 如果跳转登录页面,则移除token
    localStorage.removeItem('token')
    localStorage.removeItem('user')
    next()
  } else {
    let token = localStorage.getItem('token');
    if (token === null || token === '') { //token不存在页跳转到登录页面
      router.replace({path:'/'})
    } else {
      // 检验token是否正确
      axios({
        url: 'http://localhost:8210/checkToken', //在controller中写一个接口用来token校验
        method: 'get',
        //将token信息保存在header里
        headers: {
          token: token
        }
      }).then((response) => {
        if (!response.data) {
          console.log('检验失败')
          router.replace({path:'/'}) // 如果token失效,返回到登录页面
        }
      })
      next();
    }
  }
})

本文是根据其他博主的文章进行总结,有错误的地方希望指出!

结束!

你可能感兴趣的:(SpringBoot,spring,boot,vue.js,java)