springboot+vue结合redis实现登录拦截

springboot+vue结合redis实现登录拦截

1.后端代码

1.1pom.xml依赖

redis依赖

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>

mysql依赖

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.33</version>
		</dependency>

jwt依赖

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

完整pom.xml依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.15</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>vuesb</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>vuesb</name>
	<description>vuesb</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>com.auth0</groupId>
			<artifactId>java-jwt</artifactId>
			<version>3.8.3</version>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.33</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

1.2后端jwt拦截器

1.2.1 Interceptor拦截器配置(入口拦截)

因为我前端的url为本机8080端口,所以在allowedOrigins这里我把允许的跨域请求设置为了我的前端端口,这里也可以写为

registry.addMapping("/**")
                .allowedOrigins("*")
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH", "OPTIONS", "HEAD")
                .maxAge(3600 * 24);
package com.example.vuesb.jwt;

import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.ArrayList;

public class InterceptorConfig implements WebMvcConfigurer {
    TokenInterceptor tokenInterceptor;
    public InterceptorConfig(TokenInterceptor tokenInterceptor){//构造函数
            this.tokenInterceptor=tokenInterceptor;
        }
        @Override
        public void addInterceptors(InterceptorRegistry registry){//配置拦截器
            ArrayList<String>excludePath=new ArrayList<>();
            excludePath.add("/login");//登录
            excludePath.add("/logout");//登出
            excludePath.add("/register");//注册
            registry.addInterceptor(tokenInterceptor)//注册拦截器
                    .addPathPatterns("/**")//拦截所有请求
                    .excludePathPatterns(excludePath);//添加拦截白名单
            WebMvcConfigurer.super.addInterceptors(registry);//调用父接口
    }
    @Override
    public void addCorsMappings(CorsRegistry registry){
        registry.addMapping("/**")
                .allowCredentials(true)//允许携带cookie
                .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH", "OPTIONS", "HEAD")//允许访问的方法
                .allowedOrigins("http://localhost:8080")//允许的跨域访问地址
                .maxAge(3600*24);//options缓存时间
    }
}

1.2.2 JWTUtil

这里的verify方法没什么用,是我在用redis之前写的,可以不加

package com.example.vuesb.jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Date;


public class JWTUtil {
    private static final long EXPIRE_TIME = 60 * 60 * 1000;
    private static final String tokenPassword = "uziCjb";

    public static String sign(String username) {//用用户名作为被加密的对象
        String token;
        Date expireTime = new Date(System.currentTimeMillis() + EXPIRE_TIME);//过期时间
        token = JWT.create()//生成jwt令牌,加密过程
                .withIssuer("llh")
                .withClaim("username", username)
                .withExpiresAt(expireTime)
                .sign(Algorithm.HMAC256(tokenPassword));
        return token;//返回加密后的token
    }
    public static boolean verify(String token){
        JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256(tokenPassword)).withIssuer("llh").build();//构建一个jwt解码器
        DecodedJWT jwtToken=jwtVerifier.verify(token);//解码
        if(token.isEmpty()){//若token为空则返回false拦截
            return false;
        }
        else {
            System.out.println("认证通过:");
            System.out.println("issuer: " + jwtToken.getIssuer());
            System.out.println("username: " + jwtToken.getClaim("username").asString());
            System.out.println("过期时间:      " + jwtToken.getExpiresAt());
            return true;
        }
    }
}

1.2.3TokenInterceptor(token拦截器)

注册服务那里一定要写!!!

package com.example.vuesb.jwt;

import com.example.vuesb.redis.RedisUtil;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class TokenInterceptor implements HandlerInterceptor {
    @Resource
    RedisUtil redisUtil;//注册服务

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String token = request.getHeader("token");
        try {
            if (request.getMethod().equals("OPTIONS")) {//检查是否为跨域请求
                if (token != null) {
                    if (redisUtil.getTokens(token) != null) {
                        response.setStatus(200);//设置状态码为正常
                        return true;
                    } else {
                        response.setStatus(401);//设置状态码为未通过登录验证
                        return false;
                    }
                } else {
                    response.setStatus(401);//设置状态码为未通过登录验证
                    return false;

                }
            }
        } catch (Exception e) {
            response.setStatus(500);
            throw new RuntimeException();
        }
        response.setCharacterEncoding("utf-8");
        try {
            if (token != null) {
                if (redisUtil.getTokens(token) != null) {
                    response.setStatus(200);//设置状态码为正常,即通过登录验证
                    System.out.println("通过拦截器");
                    return true;
                } else {
                    response.setStatus(401);
                    return false;
                }
            } else {
                response.setStatus(401);
                return false;
            }
        } catch (Exception exception) {
            response.setStatus(500);//发生了不可预测的错误
            throw new RuntimeException();
        }


    }
}

1.3redis配置

RedisUtil

package com.example.vuesb.redis;


import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;


@Service
public class RedisUtil {
    @Resource
    private RedisTemplate<String, String> stringRedisTemplate;//这是一个使用redis的API,可以直接用StringRedisTemplate
    
    public void addTokens(String username, String token) {//存入token
        if (username != null && token != null) {
            System.out.println("参数不为空");
            System.out.println(username);
            System.out.println(token);
            ValueOperations valueOperations = stringRedisTemplate.opsForValue();
            valueOperations.set(username, token, 60, TimeUnit.MINUTES);//设置token过期时间为一小时

        } else {
            System.out.println("参数为空");
        }
    }

    public String getTokens(String username) {//获取token
        return stringRedisTemplate.opsForValue().get(username);
    }

    public void delTokens(String username) {//删除token
        stringRedisTemplate.delete(username);
    }

}

1.4登录接口

数据库这里我是用的原生jdbc

package com.example.vuesb;

import JDBC.VueLogin;
import com.example.vuesb.jwt.JWTUtil;
import com.example.vuesb.redis.RedisUtil;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.sql.ResultSet;
import java.sql.SQLException;

@CrossOrigin
@RestController
public class demo {
    @Resource
    RedisUtil redisUtil;
    @RequestMapping("/login")
    public String loginCheck(@RequestParam("username") String username, @RequestParam("password") String password) throws SQLException {
        String returnStr="No";
        ResultSet resultSet;
        resultSet=new VueLogin().seekAccount(username);
        String sqlPass = null;
        if(resultSet.next()){
            sqlPass=resultSet.getString(1);
            if (sqlPass.equals(password)){
                String token = new JWTUtil().sign(username);

                redisUtil.addTokens(username,token);
                returnStr=token;
            }
        }

        return returnStr;
    }
    @RequestMapping("/register")
    public String register(@RequestParam("username") String username,@RequestParam("password") String password){
        String returnStr="123";
        ResultSet resultSet=new VueLogin().seekAccount(username);
        try {
            if (!resultSet.next()){
                new VueLogin().addRegister(username, password);
                returnStr="OK";
            }
            else {
                returnStr="No";
            }
        } catch (SQLException e) {
            System.out.println("注册失败");
            throw new RuntimeException(e);
        }
        return  returnStr;
    }
}

1.5mysql

package JDBC;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class VueLogin {
    public void addRegister(String username ,String password){
        String sql="insert into vuelogin(username,password)values(?,?)";
        PreparedStatement preparedStatement;
        try {
            preparedStatement=Conn.getConnection().prepareStatement(sql);
            preparedStatement.setString(1,username);
            preparedStatement.setString(2,password);
            preparedStatement.executeUpdate();
            preparedStatement.close();
        } catch (SQLException e) {
            System.out.println("注册失败");
            throw new RuntimeException(e);

        }
    }
    public ResultSet seekAccount(String account){
        String sql="select password from vuelogin where username=?";
        PreparedStatement preparedStatement;
        ResultSet resultSet;
        try {

            preparedStatement= Conn.getConnection().prepareStatement(sql);
            preparedStatement.setString(1,account);
            resultSet=preparedStatement.executeQuery();

            return resultSet;
        } catch (SQLException e) {
            System.out.println("查询失败");
            throw new RuntimeException(e);
        }
    }
}

2.前端代码

2.1 main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './plugins/element.js'
import './router/permission'
import axiosInstance from './store/request.js'
import axios from 'axios'
Vue.prototype.$http = axios
Vue.config.productionTip = false
// axiosInstance.defaults.baseURL = 'http://localhost:8081'; // 后端端口为8081

new Vue({
  router,
  store,
  axiosInstance,
  render: h => h(App)
}).$mount('#app')

2.2store/index.js(记得安装vuex)

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    token:localStorage.getItem('token')?localStorage.getItem('token'):''
  },
  getters: {
  },
  mutations: {
    setToken(state,token){
      state.token=token
      localStorage.setItem("token",token.token)//存储token
    },
    delToken(state){
      state.token=''
      localStorage.removeItem("token")//删除token
      
    }
  },
  actions: {
  },
  modules: {
  }
})

2.3路由配置router下index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
import LayoutView from "../views/LayoutView.vue"
Vue.use(VueRouter)

const routes = [
{
  path:"/",
  name:"LayoutView",
  component:LayoutView,
  children:[
    {
      path: '',
      name: 'home',
      component: HomeView,
      meta:{
        isLogin:true
      }
    },
    {
      path:"params",
      name:"paramsview",
      component:()=>import("../views/main/ParamsView.vue"),
      meta:{
        isLogin:true
      }
    },
    {
      path:"product",
      name:"ProductView",
      component:()=>import("../views/main/ProductView.vue"),
      meta:{
        isLogin:true
      }
    },
    {
      path:"ad",
      name:"ADCategoryview",
      component:()=>import("../views/main/ADCategoryView.vue"),
      meta:{
        isLogin:true
      }
    }
  ]
},
{
  path:"/login",
  name:"LoginView",
  component:()=>import("../views/LoginView.vue")
}
  
  
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

2.4permission路由守护

import router from "./index";
//路由守护
router.beforeEach((to,from,next)=>{
    if(to.meta.isLogin){
        if(to.path==='/login'){
            next()
        }
        else{
            const tokenStr=window.localStorage.getItem('token')
            if(!tokenStr){
                return next('/login')
            }
            else{
                next()
            }
        }
    }
    else{
        next();
    }
})

 2.5设置新的全局axios

在这里需要设置一个新的全局axios,这样可以在每一个ajax请求发出之前将token加入到请求头中

import axios from "axios";


//创建axios实例
const axiosInstance = axios.create({
    baseURL: 'http://localhost:8081', // 设置默认发送地址为后端端口
    });

//添加拦截器,在每次ajax之前都会执行这个操作
axiosInstance.interceptors.request.use(function (config){
    console.log("操作了")
    //从本地缓存获得token
    const token=window.localStorage.getItem('token')
    
    //如果存在将其加入请求头中
    if(token){
        console.log(token)
        config.headers.token = token;
    }
    return config;
},function (error){
    return Promise.reject(error);//发生错误返回一个拒绝的promise对象
})

export default axiosInstance;

2.6loginView(登录业务)

上半部分是elementUI,可以省略

<template>
  <div class="login">
    <el-card class="box-card">
    <div slot="header" class="clearfix">
      <span>人工智能大数据实验室</span>

    
    </div>
    <el-tabs v-model="currentIndex" stretch @tab-click="handleClick">
      <el-tab-pane label="登录" name="login">
        <el-form :model="loginForm" :rules="rules" status-icon ref="loginForm">
          <el-form-item label="用户名" label-width="80px" prop="username">
              <el-input type="text" v-model="loginForm.username"></el-input>
          </el-form-item>
          <el-form-item label="密码" label-width="80px" prop="password">
              <el-input type="password" v-model="loginForm.password"></el-input>
          </el-form-item>
          <el-form-item >
            <el-button type="primary" @click="submitForm('loginForm')">登录</el-button>
          </el-form-item>
        </el-form>
      </el-tab-pane> 

      <el-tab-pane label="注册" name="register">
        
        <el-form :model="registerForm" :rules="rules" status-icon ref="registerForm">

          <el-form-item label="用户名" label-width="80px" prop="username">
              <el-input type="text" v-model="registerForm.username"></el-input>
          </el-form-item>

          <el-form-item label="密码" label-width="80px" prop="password">
              <el-input type="password" v-model="registerForm.password"></el-input>
          </el-form-item>

          <el-form-item label="确认密码" label-width="80px" prop="checkPassword">
              <el-input type="password" v-model="registerForm.checkPassword"></el-input>
          </el-form-item>

          <el-form-item >
            <el-button type="primary" @click="submitForm('registerForm')">注册</el-button>
          </el-form-item>
        </el-form>
      </el-tab-pane>  
    </el-tabs>
  </el-card>
  </div>
</template>

<script>
import axios from 'axios';
import axiosInstance from '@/store/request';
export default {
  data(){
    var validateUsername= (rule,value,callback) =>{
      if(!value){
        return callback(new Error('请输入账号'))
      }
      else if(value.length<3){
        return callback(new Error('请输入3位以上的账号'))
      }
      else {
        callback();
      }
    }

    var validatePassword=(rule,value,callback) =>{
        if(this.activeTab==="login"){
          if(!value){
        return callback(new Error("请输入密码"))
        }
        else{
          callback();
        }
      }
      if(this.activeTab==="register"){
        if(!value){
        return callback(new Error("请输入密码"))
      }
      else if(value.length<6){
        callback(new Error("请输入六位以上的密码"))
      }
      else if(value.length>16){
        callback(new Error("请输入16位以下的密码"))
      }
      else{
        callback();
      }
        }
      }
    var validateRegisterCheckPassword=(rule,value,callback) =>{
      if(!value){
        return callback(new Error("请再次输入密码"))
      }
      else if(this.registerForm.password!==value){
      
        return callback(new Error("两次密码输入不一致"))
      }
      else {
        return callback()
      }
    }
      return {
        currentIndex:"login",
        loginForm:{
            username:"",
            password:""
        },
        registerForm:{
            username:"",
            password:"",
            checkPassword:"",
        },
        activeTab:"login",
        rules:{
            username:[{
              validator:validateUsername,trigger:'blur'
            }],
            password:[{
              validator:validatePassword,trigger:'blur'
            }],
            checkPassword:[{
              validator:validateRegisterCheckPassword,trigger:'blur'
            }],
        }
      }
  },
 
  methods:{
    submitForm(formname){
      const that=this
        this.$refs[formname].validate((valid)=>{
            if(valid){
              if(this.activeTab==="login"){
                console.log(this.loginForm.username)
                console.log(this.loginForm.password)

                    axiosInstance.get('/login',{
                    params:{
                      username:this.loginForm.username,
                      password:this.loginForm.password,
                      
                    }
                }).then(function (response){
                  if(response.data!=='No'){
                    that.$store.commit('setToken',response.data)
                    that.$router.push({path:'/product'})
                  }
                  else{
                    alert("账号密码错误")
                  }
                })
                
                
                // console.log(this.username+"pass"+this.password);
              }
              if(this.activeTab==="register"){

                axios.get('/register',{
                  params:{
                    username:this.registerForm.username,
                    password:this.registerForm.password,
                  }
                }).then(function (response){
                  if(response.data==='OK'){
                    alert("注册成功")
                  }
                  else if(response.data==='No'){
                    alert("账号已存在")
                  }
                  else{
                    alert("注册失败")
                  }
                })

                console.log(this.registerForm)
          }
        }
            else{
              console.log("abc")
              return;
            }
        })
    },
    handleClick(tab){
      this.activeTab=tab.name
      // console.log("active"+this.activeTab)
      // console.log("tab"+tab.name)

    }
  }
}
</script>

<style>
  
  .login{
    width: 1200px;
    margin: 0 auto;
    text-align: center;
  }
  .box-card{
    width: 500px;
    margin: 100px auto;
  }
</style>

3.成果展示

可以在请求头里看到加上了一个token

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值