- token表示按照一定的规则(通用的、官方的,如JWT)生成的字符串(可以包含用户的信息)
- jwt头部信息
- 有效载荷,包含用户主体信息
- 签名哈希,防伪标志
- 在任意一个模块进行登录,登录之后按照一定的规则将登录之后的用户数据生成字符串,通过两种方法方式返回
- 可以把字符串通过cookie返回
- 把字符串加到地址栏返回
- 访问项目模块时,地址栏需要带着生成的字符串发送请求,根据字符串获取用户信息,获取成功即表示登录
- 导入依赖
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
- JWT工具类(内容较为为固定,易知放在公共模块中)
package com.atguigu.commonutils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
public class JwtUtils {
// token过期时间
public static final long EXPIRE = 1000 * 60 * 60 * 24;
// 密钥(随意写,企业中有规定的字符串)
public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";
// 生成token字符串的方法
public static String getJwtToken(String id, String nickname){
String JwtToken = Jwts.builder()
// 设置头部参数
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
// 设置分类以及过期时间
.setSubject("guli-user")
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
// 设置token主体部分,存储用户信息,不限个数(属性)
.claim("id", id)
.claim("nickname", nickname)
// 设置签名哈希,防伪标志
.signWith(SignatureAlgorithm.HS256, APP_SECRET)
.compact();
return JwtToken;
}
/**
* 判断token是否存在与有效
* @param: jwtToken
* @return
*/
public static boolean checkToken(String jwtToken) {
if(StringUtils.isEmpty(jwtToken)) return false;
try {
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 判断token是否存在与有效
* @param request
* @return
*/
public static boolean checkToken(HttpServletRequest request) {
try {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return false;
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 根据token获取会员id
* @param request
* @return
*/
public static String getMemberIdByJwtToken(HttpServletRequest request) {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return "";
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
Claims claims = claimsJws.getBody();
// 和上面的设置主体部分属性名称一样即可,获取什么可根据需求
return (String)claims.get("id");
}
}
- 注册时,填写相关信息,获取验证码调用接口的业务代码(可参考博主的“SpringBoot整合阿里云短信服务”)
- 登录成功时需要返回按照JWT(JWT工具类)规则生成的token字符串的业务代码(其中需求可改)
@Override
public String userLogin(UcenterMember member) throws MyException {
String mobile = member.getMobile();
String password = member.getPassword();
// 手机号和密码是否为空
if(StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) {
throw new MyException(20001, "登录失败!");
}
// 判断手机号是否正确
QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
wrapper.eq("mobile", mobile);
UcenterMember ucenterMember = this.getOne(wrapper);
if(ucenterMember == null) {
throw new MyException(20001, "手机号错误!");
}
// 判断密码是否正确
// 将输入密码加密与数据库校验(每个企业的加密方式不一样)
if(!MD5.encrypt(password).equals(ucenterMember.getPassword())) {
throw new MyException(20001, "密码错误!");
}
// 判断用户的状态(禁用 or 活跃)
if(ucenterMember.getIsDisabled()) {
throw new MyException(20001, "登录失败!");
}
// 按照JWT的规则生成token字符串
String id = ucenterMember.getId();
String nickname = ucenterMember.getNickname();
String token = JwtUtils.getJwtToken(id, nickname);
return token;
}
- 前端发送请求时将token带回来进行解析(JWT工具类中已有解析token的方法)获取其中的用户主题信息,返回用户的信息给前端,
// 根据token获取用户信息
@GetMapping("getMemberInfo")
public R getMemberInfo(HttpServletRequest request) {
// 调用jwt工具类的方法,根据request对象获取头信息,返回用户id
String memberId = JwtUtils.getMemberIdByJwtToken(request);
// 查询数据库根据用户id获取用户信息
UcenterMember member = memberService.getById(memberId);
return R.ok().data("memberInfo", member);
}
- 接下来单点登录的重头戏,vue先下载js-cookie(前端代码)
- 上面登录成功时返回的token字符串,前端将token字符串放到cookie里面
//第二步 获取token字符串放到cookie里面
//第一个参数cookie名称,第二个参数值,第三个参数作用范围
cookie.set('guli_token',response.data.data.token,{domain: 'localhost'})
- 创建前端拦截器,判断cookie里面是否有token字符串,如果有,就将token字符串放到header(请求头)中
import axios from 'axios';
import cookie from 'js-cookie';
// 创建axios实例
const service = axios.create({
baseURL: 'http://localhost:9001', // api的base_url
timeout: 20000 // 请求超时时间
});
//第三步 创建拦截器 http request 拦截器
service.interceptors.request.use(
config => {
//debugger
//判断cookie里面是否有名称是guli_token数据
if (cookie.get('guli_token')) {
//把获取cookie值放到header里面
config.headers['token'] = cookie.get('guli_token');
}
return config
},
err => {
return Promise.reject(err);
});
- 根据token值调用相关的接口,根据token获取用户信息,将返回的用户信息放到cookie中,为了显示页面(主页)
//第四步 调用接口 根据token获取用户信息,为了首页面显示
loginApi.getLoginUserInfo()
.then(response => {
this.loginInfo = response.data.data.memberInfo
//获取返回用户信息,放到cookie里面
cookie.set('guli_ucenter',this.loginInfo,{domain: 'localhost'})
//跳转页面
window.location.href = "/";
})
- 完整的代码大致如下
//登录的方法
submitLogin() {
//第一步 调用接口进行登录,返回token字符串
loginApi.submitLoginUser(this.user)
.then(response => {
//第二步 获取token字符串放到cookie里面
//第一个参数cookie名称,第二个参数值,第三个参数作用范围
cookie.set('guli_token',response.data.data.token,{domain: 'localhost'})
//第四步 调用接口 根据token获取用户信息,为了首页面显示
loginApi.getLoginUserInfo()
.then(response => {
// 一定要在这里转成字符串
this.loginInfo = JSON.stringify(response.data.data.memberInfo);
//获取返回用户信息,放到cookie里面
cookie.set('guli_ucenter',this.loginInfo,{domain: 'localhost'})
//跳转页面
window.location.href = "/";
})
})
}
- 从cookie的获取数据显示到页面中
var user = "cookie中存放用户的信息(上面将json格式转成了字符串类型)"
// 将信息显示到页面中
this.userInfo = JSON.parse(user);