前后端整合token的身份验证(jwt)
Token的身份验证
环境
SpringBoot+JWT+Vue+Axious+Vuex实现前后端分离下的身份登陆权限验证
整体思路
- 客户端通过用户名和密码向服务器发送请求登陆
- 服务器收到请求数据,在数据库进行查询验证
- 如果验证成功,服务器签发一个Token给客户端
- 客户端可以将Token存放到LocalStorage或者Cookie里
- 客服端设置监听,每次跳转路由,就判断 LocalStroage 中有无 Token ,没有就跳转到登录页面,有则跳转到对应路由页面
- 在Axios每次调后端接口,都要在请求头中加Token
- 在后端设置拦截器,用户登录后的每次请求都会经过这个拦截器校验Token是否有效
- 如果验证成功,则继续执行请求,返回请求到的数据
Json Web Token(JWT)
JWT 是一个开放标准(RFC 7519),它定义了一种用于简洁,自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法。JWT 可以使用 HMAC 算法或者是 RSA 的公钥**对进行签名。它具备两个特点:
-
简洁(Compact)
可以通过URL, POST 参数或者在 HTTP header 发送,因为数据量小,传输速度快
-
自包含(Self-contained)
负载中包含了所有用户所需要的信息,避免了多次查询数据库
JWT分为三个部分,分别是
-
header(头部)
-
payload(数据)
-
signature(签名)
前后端分离思路
服务器签发token后,将其传给前端,由于cookie不允许跨域,所以,在前端存入localStorage,再次向后端请求时,读取localStorage里面的token的,将其放入请求头里header里,后端配置拦截器,检验。
localStorage生命周期是永久,这意味着除非用户显示在浏览器提供的UI上清除localStorage信息,否则这些信息将永远存在。
##实现过程
后端部分
<!--jwt-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.3.0</version>
</dependency>
public class JwtUtils {
// 过期时间设置为15分钟
private static final long EXPTRE_TIME= 15 * 60 * 1000;
/**
* token私钥
*/
private static final String TOKEN_SECRET="8ae0d24822ef59d9e75745449b3501bc";
/**
* 生成签名
*/
public static String Sign(String userName,String userID){
try {
//过期时间
Date date=new Date(System.currentTimeMillis()+EXPTRE_TIME);
//私钥加密算法
Algorithm algorithm=Algorithm.HMAC256(TOKEN_SECRET);
//设置头部信息
Map<String,Object>header=new HashMap<>(2);
header.put("typ","JWT");
header.put("alg","HS256");
//附带加密的信息
return JWT.create()
.withHeader(header)
.withClaim("loginName",userName)
.withClaim("userID",userID)
.withExpiresAt(date)
.sign(algorithm);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
/**
* token解码,验证权限
*/
public static boolean verfy(String token){
try{
Algorithm algorithm=Algorithm.HMAC256(TOKEN_SECRET);
JWTVerifier verifier=JWT.require(algorithm).build();
DecodedJWT jwt=verifier.verify(token);
return true;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return false;
}
}
/**
* token提取内容
*/
public static Admin getAdmin(String token){
try{
Algorithm algorithm=Algorithm.HMAC256(TOKEN_SECRET);
JWTVerifier verifier=JWT.require(algorithm).build();
DecodedJWT jwt=verifier.verify(token);
Admin admin=new Admin();
admin.setName(jwt.getClaim("loginName").toString());
admin.setId(Integer.parseInt(jwt.getClaim("userID").toString()));
return admin;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
}
前端向后端传入name, psd,验证登录信息是否正确,如果正确则进行生成Token令牌,这里注意的一点是,提取该用户id,在JWT中,不应该在负载里面加入任何敏感的数据。像密码这样的内容就不能被放在JWT中了。如果将用户的密码放在了JWT中,那么怀有恶意的第三方通过Base64解码就能很快地知道你的密码了,所以我们这里将id和name进行生成Token,一样可以用来验证。
@Service("adminService")
public class AdminServiceImpl implements AdminService {
@Resource
private AdminMapper adminMapper;
@Override
public String adminLogin(String name,String psd) {
try {
Admin admin = new Admin();
if (StringUtil.isNotEmpty(name) && StringUtil.isNotEmpty(psd)) {
admin.setName(name);
admin.setPsd(psd);
}
//2.查询结果为空,则直接返回null
if (adminMapper.selectOne(admin) == null) {
return null;
}
//数据库匹配,查询该id
int id=admin.getId();
//查询成功,则生成token
String token = JwtUtils.Sign(name, id);
return token;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
@Autowired
private AdminService adminService;
@PostMapping("/login")
public ResponseEntity<String> adminLogin(@RequestParam("username")String userName,@RequestParam("password") String password,HttpServletRequest request,
HttpServletResponse response){
String token=this.adminService.adminLogin(userName,password);
// System.out.print(token);
if (StringUtils.isBlank(token)){
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
return ResponseEntity.ok("Bearer:" + token);
}
这里我们是重写preHandle方法,即我们前端发送请求时,在controller没有接收之前,我们进行拦截请求信息,因为前端是将Token放在请求头里的,我们只需要获取到请求头的信息,然后调用前面JwtUtils类进行验证
public class JwtInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler)throws Exception{
String authHeader=request.getHeader("Authorization");
if(authHeader==null||!authHeader.startsWith("Bearer:")){
return false;
}
//获得token
String token = authHeader.substring(7);
//验证token
return JwtUtils.verfy(token);
}
进行注册配置,Springboot的配置拦截器方式,这里可以放掉一些不需要校验token的路由
@Configuration
public class GlobalConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//添加拦截器
registry.addInterceptor(new JwtInterceptor()).excludePathPatterns("/admin/login","/admin/verify");//放掉某些特定不需要校验token的路由
}
}
前端部分
说明
这里我是用Vue-cli脚手架进行搭建,使用的是Vuetify框架
安装Vuex ,这里使用的nmp安装,在项目控制台输入下面命令
npm install vuex --save
src/store/index.js代码:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store=new Vuex.Store({
state:{
//赋值
token:localStorage.getItem('token') ? localStorage.getItem('token'):''
},
mutations:{
setToken(state,token){
state.token=token;
// 这段代码token.token , 是因为在login.vue中调用这个放法传进来的是一个对象
// (即使你觉的你传进来的是一个字符串,不知道为什么会被放到object里去),
// 传进来的对象里有token这个属性
localStorage.setItem("token",token.token);
} ,
delToken(state){
state.token='';
localStorage.removeItem("token");
}
}
})
export default store;
这里我是封装了axious,在这里面将每个axious的请求把Token添加到请求头,同时,后面使用axious时,可以通过this.$https调用,当然这里需要在main.js里进行注册配置
http.js代码
import Vue from 'vue'
import axios from 'axios'
import config from './config'
import router from "./router";
import store from "./store";
axios.defaults.baseURL = config.api; // 设置axios的基础请求路径
axios.defaults.timeout = 2000; // 设置axios的请求时间
// axios.interceptors.request.use(function (config) {
// // console.log(config);
// return config;
// })
axios.loadData = async function (url) {
const resp = await axios.get(url);
return resp.data;
};
// 添加请求拦截器
axios.interceptors.request.use(config => {
// 在发送请求之前做些什么
//判断是否存在token,如果存在将每个页面header都添加token
if(store.state.token){
config.headers.common['Authorization']=store.state.token.token
}
return config;
}, error => {
// 对请求错误做些什么
return Promise.reject(error);
});
// http response 拦截器
axios.interceptors.response.use(
response => {
return response;
},
error => {
if (error.response) {
switch (error.response.status) {
case 401:
this.$store.commit('del_token');
router.replace({
path: '/login',
query: {redirect: router.currentRoute.fullPath}//登录成功后跳入浏览的当前页面
})
}
}
return Promise.reject(error.response.data)
});
Vue.prototype.$http = axios;// 将axios添加到 Vue的原型,这样一切vue实例都可以使用该对象
然后在main.js进行注册
store是将store挂载到Vue上,后面可以用this.$store 来获取store,同理router也是。
在router文件里,进行导航守卫
// 导航守卫
// 使用 router.beforeEach 注册一个全局前置守卫,判断用户是否登陆
router.beforeEach((to, from, next) => {
if (to.path === '/login') {
next();
} else {
let token = localStorage.getItem('token');
if (token === null || token === '') {
next('/login');
} else {
axios.get("/admin/verify",{
params:{
token:token
}
}).then(resp=>{
console.log(resp.data)
if(resp.data){
console.log("没拦截")
next();
}else {
console.log("被拦截")
next('/login');
}
})
}
}
}
亲测可用,
本人vx:sun632928843,有需要的 可以加下 一起交流学习
上一篇: CDR怎么绘制一个立体文字的效果图?
推荐阅读
-
从零开始搭建前后端分离的NetCore2.2(EF Core CodeFirst+Autofac)+Vue的项目框架之七使用JWT生成Token(个人见解)
-
jwt,spring security ,feign,zuul,eureka 前后端分离 整合 实现 简单 权限管理系统 与 用户认证的实现
-
前后端整合token的身份验证(jwt)
-
从零开始搭建前后端分离的NetCore2.2(EF Core CodeFirst+Autofac)+Vue的项目框架之七使用JWT生成Token(个人见解)
-
jwt方式token验证流程及基于java的JJWT的无状态的身份验证实现详解
-
spring boot+spring security+jwt+vue整合前后端分离token权限认证项目详细过程代码 含AES加密
-
从0到1搭建前后端分离的脚手架框架之后端(四) JWT TOKEN 整合