springboot+vue结合redis实现登录拦截
redis依赖
org.springframework.boot
spring-boot-starter-data-redis
mysql依赖
mysql
mysql-connector-java
8.0.33
jwt依赖
com.auth0
java-jwt
3.8.3
完整pom.xml依赖
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.7.15
com.example
vuesb
0.0.1-SNAPSHOT
vuesb
vuesb
1.8
org.springframework.boot
spring-boot-starter-web
com.auth0
java-jwt
3.8.3
mysql
mysql-connector-java
8.0.33
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.boot
spring-boot-maven-plugin
因为我前端的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){//配置拦截器
ArrayListexcludePath=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缓存时间
}
}
这里的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;
}
}
}
注册服务那里一定要写!!!
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();
}
}
}
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 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);
}
}
数据库这里我是用的原生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;
}
}
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);
}
}
}
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')
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: {
}
})
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
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();
}
})
在这里需要设置一个新的全局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;
上半部分是elementUI,可以省略
人工智能大数据实验室
登录
注册
可以在请求头里看到加上了一个token