在spring boot和vue.js中实现token认证登录是一种常见的前后端分离的认证机制。
以下是实现这一机制的基本步骤:
后端(spring boot)
1. 创建用户实体和数据库表
首先,你需要定义一个用户实体,比如user,并为其创建相应的数据库表。
@tablefield(exist = false) private string token; // 表示我们数据库没有这个字段,但是在前端我们需要返回这个字段
2. 用户注册和登录接口
在spring boot应用中创建用于注册和登录的rest api。
@override
public user login(userpassworddto userpassworddto) {
user one = getuserinfo(userpassworddto);
if (one != null) {
beanutil.copyproperties(one, userpassworddto, true);
// 设置token
string token = tokenutils.gentoken(one.getid().tostring(), one.getpassword());
one.settoken(token);
return one;
}else{
return null;
}
}
private user getuserinfo(userpassworddto userpassworddto){
querywrapper<user> querywrapper = new querywrapper<>();
querywrapper.eq("username", userpassworddto.getusername());
querywrapper.eq("password", secureutil.md5(userpassworddto.getpassword()));
list<user> one = usermapper.selectlist(querywrapper);
if (one.size()==0){
return null;
}
return one.get(0);
}3. jwt token生成
用户登录成功后,生成一个jwt(json web token),并将其发送回客户端。
首先在pom.xml文件中导入jwt包:
<!-- jwt -->
<dependency>
<groupid>com.auth0</groupid>
<artifactid>java-jwt</artifactid>
<version>3.10.3</version>
</dependency>在config包中新建tokenutils类:
package com.lyk.xuelang.config;
import cn.hutool.core.date.dateutil;
import cn.hutool.core.util.strutil;
import com.auth0.jwt.jwt;
import com.auth0.jwt.algorithms.algorithm;
import com.lyk.xuelang.entity.user;
import com.lyk.xuelang.mapper.usermapper;
import org.springframework.stereotype.component;
import org.springframework.web.context.request.requestcontextholder;
import org.springframework.web.context.request.servletrequestattributes;
import javax.annotation.postconstruct;
import javax.annotation.resource;
import javax.servlet.http.httpservletrequest;
import java.util.date;
@component
public class tokenutils {
private static usermapper staticusermapper;
@resource
private usermapper usermapper;
@postconstruct
public void setuserservice() {
staticusermapper = usermapper;
}
/**
* 生成token
*
* @return
*/
public static string gentoken(string userid, string sign) {
return jwt.create().withaudience(userid) // 将 user id 保存到 token 里面,作为载荷
.withexpiresat(dateutil.offsethour(new date(), 2)) // 2小时后token过期
.sign(algorithm.hmac256(sign)); // 以 password 作为 token 的密钥
}
/**
* 获取当前登录的用户信息
*
* @return user对象
*/
public static user getcurrentuser() {
try {
httpservletrequest request = ((servletrequestattributes) requestcontextholder.getrequestattributes()).getrequest();
string token = request.getheader("token");
if (strutil.isnotblank(token)) {
string userid = jwt.decode(token).getaudience().get(0);
return staticusermapper.selectbyid(integer.valueof(userid));
}
} catch (exception e) {
return null;
}
return null;
}
}
4. jwt token验证
在config包下新建interceptor包,然后新建jwtinterceptor类:
package com.lyk.xuelang.config.interceptor;
import cn.hutool.core.util.strutil;
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.lyk.xuelang.common.constants;
import com.lyk.xuelang.entity.user;
import com.lyk.xuelang.exception.serviceexception;
import com.lyk.xuelang.service.iuserservice;
import org.springframework.stereotype.component;
import org.springframework.web.method.handlermethod;
import org.springframework.web.servlet.handlerinterceptor;
import javax.annotation.resource;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
@component
public class jwtinterceptor implements handlerinterceptor {
@resource
private iuserservice userservice;
@override
public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception {
string token = request.getheader("token");
if(!(handler instanceof handlermethod)){
return true;
}
// 执行认证
if (strutil.isblank(token)) {
throw new serviceexception(constants.code_401, "无token验证失败");
}
// 获取 token 中的 userid
string userid;
try {
userid = jwt.decode(token).getaudience().get(0);
} catch (jwtdecodeexception j) {
string errmsg = "token验证失败,请重新登录";
throw new serviceexception(constants.code_401, errmsg);
}
// 根据token中的userid查询数据库
user user = userservice.getbyid(userid);
if (user == null) {
throw new serviceexception(constants.code_401, "用户不存在,请重新登录");
}
// 用户密码加签验证 token
jwtverifier jwtverifier = jwt.require(algorithm.hmac256(user.getpassword())).build();
try {
jwtverifier.verify(token); // 验证token
} catch (jwtverificationexception e) {
throw new serviceexception(constants.code_401, "token验证失败,请重新登录");
}
return true;
}
}
添加自定义拦截器
在config包下新建interceptorconfig类:
package com.lyk.xuelang.config;
import com.lyk.xuelang.config.interceptor.jwtinterceptor;
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
// 加自定义拦截器jwtinterceptor,设置拦截规则
public void addinterceptors(interceptorregistry registry) {
registry.addinterceptor(jwtinterceptorl())
.addpathpatterns("/**") //拦截所有请求,通过判断token是否合法来决定是否登录
.excludepathpatterns("/login","/role/page","/**/export","/**/import");//排除这些接口,也就是说,这些接口可以放行
}
@bean
public jwtinterceptor jwtinterceptorl(){
return new jwtinterceptor();
}
}
前端(vue.js)
1. 用户界面
创建登录表单,允许用户输入用户名和密码。
<div class="login-container">
<el-card class="login-card">
<h2 class="login-title">仓库管理系统</h2>
<el-form :model="loginform" :rules="rules" ref="loginform" label-width="20">
<el-form-item label="用户名" prop="username">
<el-input v-model="loginform.username" placeholder="请输入用户名"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" v-model="loginform.password" placeholder="请输入密码"></el-input>
</el-form-item>
<el-form-item style="margin: 10px 0;text-align: right;" label="温馨提示:忘记密码?联系管理员!">
<el-button type="primary" autocomplete="off" @click="submitform">登录</el-button>
</el-form-item>
</el-form>
</el-card>
</div>2. 发送登录请求
用户提交表单后,前端发送登录请求到后端的登录接口。
login.js代码如下:
import request from '@/utils/request'
// 用户登录
export function login (user) {
return request({
url: '/login',
method: 'post',
data: user
})
}home.vue页面代码如下:
<script>
import { login } from '@/api/login'
export default {
data () {
return {
loginform: {
username: '',
password: ''
},
rules: {
username: [
{ required: true, message: '请输入用户名!', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码!', trigger: 'blur' }
]
}
}
},
methods: {
submitform () {
this.$refs.loginform.validate(async (valid) => {
if (valid) {
// 在这里添加你的登录逻辑
const res = await login(this.loginform)
if (res.code === 200) {
this.$message.success('登录成功')
localstorage.setitem('user', json.stringify(res.data)) // 存储用户信息到浏览器
localstorage.setitem('token', json.stringify(res.data.token))
this.$router.push('/main')
} else {
this.$message.error(res.msg)
}
} else {
this.$message.error('账号密码错误,请重新输入!')
return false
}
})
}
}
}
</script>3. 接收并存储token
登录成功后,前端接收jwt token,并将其存储在本地存储(localstorage)或vuex状态管理中。
// 在这里添加你的登录逻辑
const res = await login(this.loginform)
if (res.code === 200) {
this.$message.success('登录成功')
localstorage.setitem('user', json.stringify(res.data)) // 存储用户信息到浏览器
localstorage.setitem('token', json.stringify(res.data.token))
this.$router.push('/main')
}4. 发送请求时携带token
在发送需要认证的请求时,前端需要在请求头中携带jwt token。
// 添加请求拦截器,一下内容是axios的拦截器,可以不用写
request.interceptors.request.use(config => {
config.headers['content-type'] = 'application/json;charset=utf-8'
const user = localstorage.getitem('user') ? json.parse(localstorage.getitem('user')) : null
if (user) {
config.headers.token = user.token
}
return config
}, error => {
return promise.reject(error)
})5. 路由守卫
使用vue router的路由守卫来保护需要认证的路由。
{ path: '/dashboard',
component: dashboard,
meta:
{requiresauth: true}
} // 标记需要验证的路由router.beforeeach((to, from, next) => {
const auth = require('@/router/auth').default // 引入认证守卫
auth.redirectifnotauthenticated(to, from, next)
})auth.js代码如下:
export default {
isauthenticated () {
// 这里应该根据你的应用逻辑来检查用户是否登录
// 例如,检查本地存储(localstorage)中是否有token
return localstorage.getitem('token') !== null
},
redirectifnotauthenticated (to, from, next) {
if (!this.isauthenticated()) {
to.path !== '/login' && to.matched.some(record => record.meta.requiresauth) ? next({ path: '/login' }) : next()
} else {
next()
}
}
}
注意事项
- 安全性:确保使用https来传输jwt token。
- token存储:考虑使用httponly的cookie来存储token,以避免xss攻击。
- token过期:jwt token应该有过期时间,并且后端需要处理token的刷新。
这只是一个简单的示例,实际应用中可能需要更复杂的逻辑,比如密码加密、token刷新机制、用户角色和权限管理等。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论