项目8:用户注册和登录的前后端联调
1.前端项目使用
2.前端项目注册模块
3.后端完成项目注册
4.前端项目登录模块
5.后端完成项目登录
6.用户认证(校验用户是否登录)
项目8:用户注册和登录的前后端联调
1.前端项目使用
- 直接使用提供的srb-site前端页面
2.前端项目注册模块
①前端项目注册模块逻辑
- 注册(投资人1 借款人2)
- 利用手机号发送验证码注册
- 输入密码点击下一步
- 注册成功
②前端页面
pages\register.vue
- 封装的数据
userinfo(userType,mobile,code,password) - 前端页面
<template>
<!--注册-->
<div class="wrap">
<div v-if="step === 1" class="tdbModule register">
<div class="registerTitle">注册账户</div>
<div class="registerLc1">
<p class="p1">填写账户信息</p>
<p class="p2">注册成功</p>
</div>
<div class="registerCont">
<ul>
<li>
<span class="dis"></span>
<input v-model="userInfo.userType" type="radio" value="1" />
我要投资
<input v-model="userInfo.userType" type="radio" value="2" />
我要借钱
</li>
<li class="telNumber">
<span class="dis">手机号码</span>
<input class="input" v-model="userInfo.mobile" />
<!--没发验证码显式上面的v-if,发了显示下面的v-else-->
<button v-if="!sending" class="button" @click="send()">
获取验证码
</button>
<button v-else disabled class="button disabled">
{{ leftSecond }}秒后重发
</button>
</li>
<li>
<span class="dis">短信验证码</span>
<input class="input" v-model="userInfo.code" />
<span class="info">
请输入验证码
</span>
</li>
<li>
<span class="dis">密码</span>
<input type="password" v-model="userInfo.password" class="input" />
<span class="info">
6-24个字符,英文、数字组成,区分大小写
</span>
</li>
<li class="agree">
<input type="checkbox" checked />
我同意《<NuxtLink to="#" target="_black">尚融宝注册协议</NuxtLink>》
<span>请查看协议</span>
</li>
<li class="btn">
<button @click="register()">
下一步
</button>
</li>
</ul>
</div>
</div>
<div v-if="step === 2" class="tdbModule register">
<div class="registerTitle">注册账户</div>
<div class="registerLc2">
<p class="p1">填写账户信息</p>
<p class="p2">注册成功</p>
</div>
<div class="registerCont">
<ul>
<li class="scses">
{{ this.userInfo.mobile }} 恭喜您注册成功!
<NuxtLink class="blue" to="/login">
请登录
</NuxtLink>
</li>
</ul>
</div>
</div>
</div>
</template>
<script>
import '~/assets/css/register.css'
export default {
data() {
return {
step: 1, //注册步骤
userInfo: {
userType: 1,
},
sending: false, // 是否发送验证码
second: 10, // 倒计时间
leftSecond: 0, //剩余时间
}
},
methods: {
//发短信
send() {
//防止空手机号输入
if (!this.userInfo.mobile) {
this.$message.error('请输入手机号')
return
}
//防止重复提交
if (this.sending) return
this.sending = true
//倒计时
this.timeDown()
//调用后端接口
this.$axios
.$get('/api/sms/send/' + this.userInfo.mobile)
.then((response) => {
//前端响应框
this.$message.success(response.message)
})
},
//倒计时
timeDown() {
console.log('进入倒计时')
this.leftSecond = this.second
//计时器,每隔一秒,执行一次函数
const timmer = setInterval(() => {
this.leftSecond--
if (this.leftSecond <= 0) {
clearInterval(timmer)
this.sending = false
}
}, 1000)
},
//注册
register() {
this.$axios
.$post('/api/core/userInfo/register', this.userInfo)
.then((response) => {
//注册成功表单
this.step = 2
})
},
},
}
</script>
3.后端完成项目注册
流程
测试过程中关闭sms的具体使用aliyun发送短信的业务,直接存入redis即可,省钱。。
后端接收到前端封装的数据,需要校验短信验证码,需要保存数据到数据库
①数据库设计(前端注册后需要插入的表)
user_info
user_account
user_account的user_id和user_info的user_id绑定
②创建vo对象(方便封装前端穿过的表单)
package com.atguigu.srb.core.pojo.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(description="注册对象")
public class RegisterVO {
@ApiModelProperty(value = "用户类型")
private Integer userType;
@ApiModelProperty(value = "手机号")
private String mobile;
@ApiModelProperty(value = "验证码")
private String code;
@ApiModelProperty(value = "密码")
private String password;
}
③编写注册controller(api让前端调用)
package com.atguigu.srb.core.controller.api;
import com.atguigu.common.exception.Assert;
import com.atguigu.common.result.R;
import com.atguigu.common.result.ResponseEnum;
import com.atguigu.common.util.RegexValidateUtils;
import com.atguigu.srb.core.pojo.vo.RegisterVO;
import com.atguigu.srb.core.service.UserInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* <p>
* 用户基本信息 前端控制器
* </p>
*
* @author Likejin
* @since 2023-04-09
*/
@Api(tags = "会员接口")
@RestController
@RequestMapping("/api/core/userInfo")
@Slf4j
@CrossOrigin
public class UserInfoController {
@Resource
private RedisTemplate redisTemplate;
@Resource
private UserInfoService userInfoService;
@ApiOperation("会员注册")
@PostMapping("/register")
public R register(
@RequestBody RegisterVO registerVO){
String mobile = registerVO.getMobile();
String password = registerVO.getPassword();
String code = registerVO.getCode();
//校验验证码是否正确
String codeGen = (String)redisTemplate.opsForValue().get("srb:sms:code:" + mobile);
//防止无脑攻击
Assert.notEmpty(mobile,ResponseEnum.MOBILE_NULL_ERROR);
Assert.notEmpty(password,ResponseEnum.PASSWORD_NULL_ERROR);
Assert.notEmpty(code,ResponseEnum.CODE_NULL_ERROR);
Assert.isTrue(RegexValidateUtils.checkCellphone(mobile),ResponseEnum.MOBILE_ERROR);
//没匹配成功报错
Assert.equals(code,codeGen, ResponseEnum.CODE_ERROR);
//注册
userInfoService.register(registerVO);
return R.ok().message("注册成功");
}
}
④业务层service调用
package com.atguigu.srb.core.service;
import com.atguigu.srb.core.pojo.entity.UserInfo;
import com.atguigu.srb.core.pojo.vo.RegisterVO;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 用户基本信息 服务类
* </p>
*
* @author Likejin
* @since 2023-04-09
*/
public interface UserInfoService extends IService<UserInfo> {
void register(RegisterVO registerVO);
}
package com.atguigu.srb.core.service.impl;
import com.atguigu.common.exception.Assert;
import com.atguigu.common.result.ResponseEnum;
import com.atguigu.common.util.MD5;
import com.atguigu.srb.core.mapper.UserAccountMapper;
import com.atguigu.srb.core.mapper.UserInfoMapper;
import com.atguigu.srb.core.pojo.entity.UserAccount;
import com.atguigu.srb.core.pojo.entity.UserInfo;
import com.atguigu.srb.core.pojo.vo.RegisterVO;
import com.atguigu.srb.core.service.UserInfoService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
/**
* <p>
* 用户基本信息 服务实现类
* </p>
*
* @author Likejin
* @since 2023-04-09
*/
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {
@Resource
private UserAccountMapper userAccountMapper;
@Transactional(rollbackFor = Exception.class)
@Override
public void register(RegisterVO registerVO) {
//判断当前用户是否已经被注册
QueryWrapper<UserInfo> userInfoQueryWrapper = new QueryWrapper<>();
userInfoQueryWrapper.eq("mobile",registerVO.getMobile());
//baseMapper被封装为UserInfoMapper
Integer count = baseMapper.selectCount(userInfoQueryWrapper);
Assert.isTrue(count==0, ResponseEnum.MOBILE_EXIST_ERROR);
//插入用户信息记录 user_info
UserInfo userInfo = new UserInfo();
userInfo.setUserType(registerVO.getUserType());
userInfo.setNickName(registerVO.getMobile());
userInfo.setName(registerVO.getMobile());
userInfo.setMobile(registerVO.getMobile());
userInfo.setPassword(MD5.encrypt(registerVO.getPassword()));
userInfo.setStatus(UserInfo.STATUS_NORMAL); //正常
userInfo.setHeadImg(UserInfo.USER_AVATAR);
baseMapper.insert(userInfo);
//插入用户账户记录 user_account
UserAccount userAccount = new UserAccount();
userAccount.setUserId(userInfo.getId());
userAccountMapper.insert(userAccount);
}
}
⑥细节
- 修改UserInfo增加常量啊
//上锁状态正常为1,不正常为0
public static final Integer STATUS_NORMAL = 1;
public static final Integer STATUS_LOCKED = 0;
//头像地址
public static final String USER_AVATAR ="https://srb-file-200921-lkj.oss-cn-beijing.aliyuncs.com/test/2023/04/12/4a50f632-8111-4c18-9232-2e58eee69bfe.png";
- 修改号码验证让自己的手机号好使
String regex = "^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0-9]))\\d{8}$";
- 引入加密工具加密密码
package com.atguigu.common.util;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public final class MD5 {
public static String encrypt(String strSrc) {
try {
char hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'a', 'b', 'c', 'd', 'e', 'f' };
byte[] bytes = strSrc.getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(bytes);
bytes = md.digest();
int j = bytes.length;
char[] chars = new char[j * 2];
int k = 0;
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
chars[k++] = hexChars[b >>> 4 & 0xf];
chars[k++] = hexChars[b & 0xf];
}
return new String(chars);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("MD5加密出错!!+" + e);
}
}
}
4.前端项目登录模块
①实现流程
- 用户选择投资人,借款人
- 输入手机号密码 立即登录
②前端页面
- 封装的数据
userInfo(userType,mobile,password) - 前端页面
pages\login.vue
将token保存到cookie中
<template>
<!--登录-->
<div class="wrap">
<div class="tdbModule loginPage">
<div class="registerTitle">用户登录</div>
<div class="registerCont">
<ul>
<li>
<span class="dis"></span>
<input v-model="userInfo.userType" type="radio" value="1" />
投资人
<input v-model="userInfo.userType" type="radio" value="2" />
借款人
</li>
<li>
<span class="dis">手机号:</span>
<input class="input" v-model="userInfo.mobile" />
</li>
<li>
<span class="dis">密码:</span>
<input class="input" v-model="userInfo.password" type="password" />
</li>
<li class="btn">
<button @click="login()" :class="{ disabled: !isValid }">
立即登录
</button>
</li>
</ul>
</div>
</div>
</div>
</template>
<script>
import '~/assets/css/register.css'
//引入cookie模块
import cookie from 'js-cookie'
export default {
data() {
return {
userInfo: {
userType: 1,
},
isValid: true, //表单校验是否成功
}
},
methods: {
//登录
login() {
this.$axios
.$post('/api/core/userInfo/login', this.userInfo)
.then((response) => {
console.log(response.data)
//利用cookie存token(后面只需要验证token即可)
cookie.set('userInfo', response.data.userInfo)
window.location.href = '/user'
})
},
},
}
</script>
5.后端完成项目登录
①引入jwt包和jwt工具
<!--访问令牌-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
</dependencies>
package com.atguigu.srb.base.util;
import com.atguigu.common.exception.BusinessException;
import com.atguigu.common.result.ResponseEnum;
import io.jsonwebtoken.*;
import org.springframework.util.StringUtils;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Date;
public class JwtUtils {
private static long tokenExpiration = 24*60*60*1000;
private static String tokenSignKey = "A1t2g3uigu123456";
/**
* @param :
* @return Key
* @author Likejin
* @description 生成了一个Key类型的秘钥
* @date 2023/4/13 15:38
*/
private static Key getKeyInstance(){
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
byte[] bytes = DatatypeConverter.parseBase64Binary(tokenSignKey);
return new SecretKeySpec(bytes,signatureAlgorithm.getJcaName());
}
/**
* @param userId:
* @param userName:
* @return String
* @author Likejin
* @description 创建token
* @date 2023/4/13 15:37
*/
public static String createToken(Long userId, String userName) {
String token = Jwts.builder()
.setSubject("SRB-USER")
.setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
.claim("userId", userId)
.claim("userName", userName)
.signWith(SignatureAlgorithm.HS512, getKeyInstance())
.compressWith(CompressionCodecs.GZIP)
.compact();
return token;
}
/**
* 判断token是否有效
* @param token
* @return
*/
public static boolean checkToken(String token) {
if(StringUtils.isEmpty(token)) {
return false;
}
try {
Jwts.parser().setSigningKey(getKeyInstance()).parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
public static Long getUserId(String token) {
Claims claims = getClaims(token);
Integer userId = (Integer)claims.get("userId");
return userId.longValue();
}
public static String getUserName(String token) {
Claims claims = getClaims(token);
return (String)claims.get("userName");
}
public static void removeToken(String token) {
//jwttoken无需删除,客户端扔掉即可。
}
/**
* 校验token并返回Claims
* @param token
* @return
*/
private static Claims getClaims(String token) {
if(StringUtils.isEmpty(token)) {
// LOGIN_AUTH_ERROR(-211, "未登录"),
throw new BusinessException(ResponseEnum.LOGIN_AUTH_ERROR);
}
try {
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(getKeyInstance()).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
return claims;
} catch (Exception e) {
throw new BusinessException(ResponseEnum.LOGIN_AUTH_ERROR);
}
}
}
②写前端传来的vo对象
package com.atguigu.srb.core.pojo.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author Likejin
* @description 封装登录对象
* @date 2023/4/13 15:42
*/
@Data
@ApiModel(description="登录对象")
public class LoginVO {
@ApiModelProperty(value = "用户类型")
private Integer userType;
@ApiModelProperty(value = "手机号")
private String mobile;
@ApiModelProperty(value = "密码")
private String password;
}
③前端展示用户信息封装vo
package com.atguigu.srb.core.pojo.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author Likejin
* @description 展示用户信息类
* @date 2023/4/13 15:42
*/
@Data
@ApiModel(description="用户信息对象")
public class UserInfoVO {
@ApiModelProperty(value = "用户姓名")
private String name;
@ApiModelProperty(value = "用户昵称")
private String nickName;
@ApiModelProperty(value = "头像")
private String headImg;
@ApiModelProperty(value = "手机号")
private String mobile;
@ApiModelProperty(value = "1:出借人 2:借款人")
private Integer userType;
@ApiModelProperty(value = "JWT访问令牌")
private String token;
}
④controller层的编写
package com.atguigu.srb.core.controller.api;
import com.atguigu.common.exception.Assert;
import com.atguigu.common.result.R;
import com.atguigu.common.result.ResponseEnum;
import com.atguigu.common.util.RegexValidateUtils;
import com.atguigu.srb.core.pojo.vo.LoginVO;
import com.atguigu.srb.core.pojo.vo.RegisterVO;
import com.atguigu.srb.core.pojo.vo.UserInfoVO;
import com.atguigu.srb.core.service.UserInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
/**
* <p>
* 用户基本信息 前端控制器
* </p>
*
* @author Likejin
* @since 2023-04-09
*/
@Api(tags = "会员接口")
@RestController
@RequestMapping("/api/core/userInfo")
@Slf4j
@CrossOrigin
public class UserInfoController {
@Resource
private RedisTemplate redisTemplate;
@Resource
private UserInfoService userInfoService;
@ApiOperation("会员注册")
@PostMapping("/register")
public R register(
@RequestBody RegisterVO registerVO){
String mobile = registerVO.getMobile();
String password = registerVO.getPassword();
String code = registerVO.getCode();
//校验验证码是否正确
String codeGen = (String)redisTemplate.opsForValue().get("srb:sms:code:" + mobile);
//防止无脑攻击
Assert.notEmpty(mobile,ResponseEnum.MOBILE_NULL_ERROR);
Assert.notEmpty(password,ResponseEnum.PASSWORD_NULL_ERROR);
Assert.notEmpty(code,ResponseEnum.CODE_NULL_ERROR);
Assert.isTrue(RegexValidateUtils.checkCellphone(mobile),ResponseEnum.MOBILE_ERROR);
//没匹配成功报错
Assert.equals(code,codeGen, ResponseEnum.CODE_ERROR);
//注册
userInfoService.register(registerVO);
return R.ok().message("注册成功");
}
@ApiOperation("会员登录")
@PostMapping("/login")
public R login(@RequestBody LoginVO loginVO, HttpServletRequest request){
String mobile = loginVO.getMobile();
String password = loginVO.getPassword();
Assert.notEmpty(mobile,ResponseEnum.MOBILE_NULL_ERROR);
Assert.notEmpty(password,ResponseEnum.PASSWORD_NULL_ERROR);
//记录用户登录日志(user_login_record)
String ip = request.getRemoteAddr();
UserInfoVO userInfoVO = userInfoService.login(loginVO,ip);
return R.ok().data("userInfo",userInfoVO);
}
}
⑤service层的编写
package com.atguigu.srb.core.service;
import com.atguigu.srb.core.pojo.entity.UserInfo;
import com.atguigu.srb.core.pojo.vo.LoginVO;
import com.atguigu.srb.core.pojo.vo.RegisterVO;
import com.atguigu.srb.core.pojo.vo.UserInfoVO;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 用户基本信息 服务类
* </p>
*
* @author Likejin
* @since 2023-04-09
*/
public interface UserInfoService extends IService<UserInfo> {
void register(RegisterVO registerVO);
UserInfoVO login(LoginVO loginVO, String ip);
}
package com.atguigu.srb.core.service.impl;
import com.atguigu.common.exception.Assert;
import com.atguigu.common.result.ResponseEnum;
import com.atguigu.common.util.MD5;
import com.atguigu.srb.base.util.JwtUtils;
import com.atguigu.srb.core.mapper.UserAccountMapper;
import com.atguigu.srb.core.mapper.UserInfoMapper;
import com.atguigu.srb.core.mapper.UserLoginRecordMapper;
import com.atguigu.srb.core.pojo.entity.UserAccount;
import com.atguigu.srb.core.pojo.entity.UserInfo;
import com.atguigu.srb.core.pojo.entity.UserLoginRecord;
import com.atguigu.srb.core.pojo.vo.LoginVO;
import com.atguigu.srb.core.pojo.vo.RegisterVO;
import com.atguigu.srb.core.pojo.vo.UserInfoVO;
import com.atguigu.srb.core.service.UserInfoService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
/**
* <p>
* 用户基本信息 服务实现类
* </p>
*
* @author Likejin
* @since 2023-04-09
*/
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {
@Resource
private UserAccountMapper userAccountMapper;
@Resource
private UserLoginRecordMapper userLoginRecordMapper;
@Transactional(rollbackFor = Exception.class)
@Override
public void register(RegisterVO registerVO) {
//判断当前用户是否已经被注册
QueryWrapper<UserInfo> userInfoQueryWrapper = new QueryWrapper<>();
userInfoQueryWrapper.eq("mobile",registerVO.getMobile());
//baseMapper被封装为UserInfoMapper
Integer count = baseMapper.selectCount(userInfoQueryWrapper);
Assert.isTrue(count==0, ResponseEnum.MOBILE_EXIST_ERROR);
//插入用户信息记录 user_info
UserInfo userInfo = new UserInfo();
userInfo.setUserType(registerVO.getUserType());
userInfo.setNickName(registerVO.getMobile());
userInfo.setName(registerVO.getMobile());
userInfo.setMobile(registerVO.getMobile());
userInfo.setPassword(MD5.encrypt(registerVO.getPassword()));
userInfo.setStatus(UserInfo.STATUS_NORMAL); //正常
userInfo.setHeadImg(UserInfo.USER_AVATAR);
baseMapper.insert(userInfo);
//插入用户账户记录 user_account
UserAccount userAccount = new UserAccount();
userAccount.setUserId(userInfo.getId());
userAccountMapper.insert(userAccount);
}
/**
* @param loginVO:
* @param ip:
* @return UserInfoVO
* @author Likejin
* @description 用户登录功能
* @date 2023/4/13 16:17
*/
@Transactional(rollbackFor = Exception.class)
@Override
public UserInfoVO login(LoginVO loginVO, String ip) {
String mobile = loginVO.getMobile();
String password = loginVO.getPassword();
Integer userType = loginVO.getUserType();
//用户名是否存在
QueryWrapper<UserInfo> userInfoQueryWrapper = new QueryWrapper<>();
userInfoQueryWrapper
.eq("mobile",mobile)
.eq("User_type",userType);
UserInfo userInfo = baseMapper.selectOne(userInfoQueryWrapper);
Assert.notNull(userInfo,ResponseEnum.LOGIN_MOBILE_ERROR);
//密码是否正确
Assert.equals(MD5.encrypt(password),userInfo.getPassword(),ResponseEnum.LOGIN_PASSWORD_ERROR);
//用户是否被禁用
Assert.equals(userInfo.getStatus(),UserInfo.STATUS_NORMAL,ResponseEnum.LOGIN_LOKED_ERROR);
//记录登录日志
UserLoginRecord userLoginRecord = new UserLoginRecord();
userLoginRecord.setUserId(userInfo.getId());
userLoginRecord.setIp(ip);
userLoginRecordMapper.insert(userLoginRecord);
//生成token
String token = JwtUtils.createToken(userInfo.getId(), userInfo.getPassword());
//组装UserInfoVo
UserInfoVO userInfoVO = new UserInfoVO();
userInfoVO.setToken(token);
userInfoVO.setName(userInfo.getName());
userInfoVO.setNickName(userInfo.getNickName());
userInfoVO.setHeadImg(userInfo.getHeadImg());
userInfoVO.setMobile(userInfo.getMobile());
userInfoVO.setUserType(userInfo.getUserType());
return userInfoVO;
}
}
结果
前端保存了后端传来的token
6.用户认证(校验用户是否登录)
①如何校验
- 登录后后端返回userinfo数据(包含token数据,保存在cookie中)
- 前端在其APPHeader中展示
如果登录展示用户数据
如果为登录展示注册或登录 - 每次在APPHeader渲染完成之后
发送ajax请求,有token则校验token然后展示用户信息 - 因为每次许多服务都需要去验证token是否登录,将token信息写入请求头中统一写在请求拦截器中
- 实现功能
只要发送ajax请求,就判断cookie中是否有token,如果有token就将其放在请求头中,需要校验token的就调用校验接口校验成功后实现逻辑
②前端代码
components\AppHeader.vue
<template>
<header>
<div class="header-top min-width">
<div class="container fn-clear">
<strong class="fn-left">
咨询热线:400-000-0000
<span class="s-time">服务时间:9:00 - 18:00</span>
</strong>
<ul class="header_contact">
<li class="c_1">
<a class="ico_head_weixin" id="wx"></a>
</li>
<li class="c_2">
<a href="#" target="_blank" title="官方QQ" alt="官方QQ">
<b class="ico_head_QQ"></b>
</a>
</li>
<li class="c_4">
<a href="#" target="_blank" title="新浪微博" alt="新浪微博">
<b class="ico_head_sina"></b>
</a>
</li>
</ul>
<!-- 用户未登录 -->
<ul v-if="!userInfo" class="fn-right header-top-ul">
<!-- <li><a href="" :class="'c-orange'">测试</a></li> -->
<li>
<NuxtLink to="/" :class="{ 'c-orange': $route.fullPath === '/' }"
>返回首页</NuxtLink
>
</li>
<li>
<div class="">
<NuxtLink
to="/register"
:class="{ 'c-orange': $route.fullPath === '/register' }"
>
免费注册
</NuxtLink>
</div>
</li>
<li>
<div class="">
<NuxtLink
to="/login"
:class="{ 'c-orange': $route.fullPath === '/login' }"
>
登录
</NuxtLink>
</div>
</li>
</ul>
<!-- 用户已登录 -->
<ul v-if="userInfo" class="fn-right header-top-ul">
<li><NuxtLink to="/" class="app">返回首页</NuxtLink></li>
<li>
<div class="">
<NuxtLink to="/user" class="user" title="我的账户">
<i class="el-icon-user-solid">{{ userInfo.nickName }}</i>
</NuxtLink>
</div>
</li>
<li>
<div class="">
<a
href="javascript:void(0)"
class="js-login"
@click="logout()"
title="退出"
>
退出
</a>
</div>
</li>
</ul>
</div>
</div>
<div class="header min-width">
<div class="container">
<div class="fn-left logo">
<NuxtLink to="/">
<img src="~/assets/images/logo.png" title="" />
</NuxtLink>
</div>
<ul class="top-nav fn-clear">
<li :class="{ on: $route.fullPath === '/' }">
<NuxtLink to="/">首页</NuxtLink>
</li>
<li :class="{ on: $route.fullPath === '/list' }">
<NuxtLink to="/lend"> 我要投资 </NuxtLink>
</li>
<li :class="{ on: $route.fullPath === '/help' }">
<NuxtLink to="/help">安全保障</NuxtLink>
</li>
<li :class="{ on: $route.fullPath === '/about' }">
<NuxtLink to="/about">关于我们</NuxtLink>
</li>
</ul>
</div>
</div>
</header>
</template>
<script>
import '~/assets/font/iconfont.css'
import cookie from 'js-cookie'
export default {
data() {
return {
userInfo: null,
}
},
mounted() {
//如果写created的话,需要在浏览器渲染之后,在能获得cookie
this.showInfo()
},
methods: {
//显示用户信息
//1.从cookie中获取用户信息,如果不存在用户信息则用户未登录
//2.存在用户信息则需要校验token是否为合法token
showInfo() {
//判断coolie中有没有值
let userinfo = cookie.get('userInfo')
if (!userinfo) {
console.log('coolie不存在')
this.userInfo = null
return
}
userinfo = JSON.parse(userinfo)
//校验token是否合法
this.$axios({
url: '/api/core/userInfo/checkToken',
method: 'get',
//因为每次访问许多服务都需要校验,直接写在请求拦截器中
headers: {
token: userinfo.token,
},
}).then((response) => {
console.log('校验成功')
this.userInfo = userinfo
})
},
//退出,需要销毁cookie
logout() {
cookie.set('userInfo', '')
window.location.href = '/login'
},
},
}
</script>
plugins\axios.js请求拦截器
$axios.onRequest((config) => {
let userInfo = cookie.get('userInfo')
if (userInfo) {
// debugger
userInfo = JSON.parse(userInfo)
config.headers['token'] = userInfo.token
}
console.log('Making request to ' + config.url)
})
③后端校验token
package com.atguigu.srb.core.controller.api;
import com.atguigu.common.exception.Assert;
import com.atguigu.common.result.R;
import com.atguigu.common.result.ResponseEnum;
import com.atguigu.common.util.RegexValidateUtils;
import com.atguigu.srb.base.util.JwtUtils;
import com.atguigu.srb.core.pojo.vo.LoginVO;
import com.atguigu.srb.core.pojo.vo.RegisterVO;
import com.atguigu.srb.core.pojo.vo.UserInfoVO;
import com.atguigu.srb.core.service.UserInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
/**
* <p>
* 用户基本信息 前端控制器
* </p>
*
* @author Likejin
* @since 2023-04-09
*/
@Api(tags = "会员接口")
@RestController
@RequestMapping("/api/core/userInfo")
@Slf4j
@CrossOrigin
public class UserInfoController {
@Resource
private RedisTemplate redisTemplate;
@Resource
private UserInfoService userInfoService;
@ApiOperation("会员注册")
@PostMapping("/register")
public R register(
@RequestBody RegisterVO registerVO){
String mobile = registerVO.getMobile();
String password = registerVO.getPassword();
String code = registerVO.getCode();
//校验验证码是否正确
String codeGen = (String)redisTemplate.opsForValue().get("srb:sms:code:" + mobile);
//防止无脑攻击
Assert.notEmpty(mobile,ResponseEnum.MOBILE_NULL_ERROR);
Assert.notEmpty(password,ResponseEnum.PASSWORD_NULL_ERROR);
Assert.notEmpty(code,ResponseEnum.CODE_NULL_ERROR);
Assert.isTrue(RegexValidateUtils.checkCellphone(mobile),ResponseEnum.MOBILE_ERROR);
//没匹配成功报错
Assert.equals(code,codeGen, ResponseEnum.CODE_ERROR);
//注册
userInfoService.register(registerVO);
return R.ok().message("注册成功");
}
@ApiOperation("会员登录")
@PostMapping("/login")
public R login(@RequestBody LoginVO loginVO, HttpServletRequest request){
String mobile = loginVO.getMobile();
String password = loginVO.getPassword();
Assert.notEmpty(mobile,ResponseEnum.MOBILE_NULL_ERROR);
Assert.notEmpty(password,ResponseEnum.PASSWORD_NULL_ERROR);
//记录用户登录日志(user_login_record)
String ip = request.getRemoteAddr();
UserInfoVO userInfoVO = userInfoService.login(loginVO,ip);
return R.ok().data("userInfo",userInfoVO);
}
/**
* @param request:
* @return R
* @author Likejin
* @description 后端得到cookie中存储的token
* @date 2023/4/13 16:28
*/
@ApiOperation("校验令牌")
@GetMapping("/checkToken")
public R checkToken(HttpServletRequest request) {
//获取请求头中的token
String token = request.getHeader("token");
boolean result = JwtUtils.checkToken(token);
if(result){
return R.ok();
}else{
//LOGIN_AUTH_ERROR(-211, "未登录"),
return R.setResult(ResponseEnum.LOGIN_AUTH_ERROR);
}
}
}
④实现结果