项目8:用户注册和登录的前后端联调

项目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) returnthis.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 {@Resourceprivate RedisTemplate redisTemplate;@Resourceprivate 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 {@Resourceprivate UserAccountMapper userAccountMapper;@Transactional(rollbackFor = Exception.class)@Overridepublic void register(RegisterVO registerVO) {//判断当前用户是否已经被注册QueryWrapper<UserInfo> userInfoQueryWrapper = new QueryWrapper<>();userInfoQueryWrapper.eq("mobile",registerVO.getMobile());//baseMapper被封装为UserInfoMapperInteger count = baseMapper.selectCount(userInfoQueryWrapper);Assert.isTrue(count==0, ResponseEnum.MOBILE_EXIST_ERROR);//插入用户信息记录 user_infoUserInfo 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_accountUserAccount userAccount = new UserAccount();userAccount.setUserId(userInfo.getId());userAccountMapper.insert(userAccount);}
}

⑥细节

  • 修改UserInfo增加常量啊
//上锁状态正常为1,不正常为0public 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 {@Resourceprivate RedisTemplate redisTemplate;@Resourceprivate 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 {@Resourceprivate UserAccountMapper userAccountMapper;@Resourceprivate UserLoginRecordMapper userLoginRecordMapper;@Transactional(rollbackFor = Exception.class)@Overridepublic void register(RegisterVO registerVO) {//判断当前用户是否已经被注册QueryWrapper<UserInfo> userInfoQueryWrapper = new QueryWrapper<>();userInfoQueryWrapper.eq("mobile",registerVO.getMobile());//baseMapper被封装为UserInfoMapperInteger count = baseMapper.selectCount(userInfoQueryWrapper);Assert.isTrue(count==0, ResponseEnum.MOBILE_EXIST_ERROR);//插入用户信息记录 user_infoUserInfo 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_accountUserAccount 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)@Overridepublic 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);//生成tokenString token = JwtUtils.createToken(userInfo.getId(), userInfo.getPassword());//组装UserInfoVoUserInfoVO 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=""><NuxtLinkto="/register":class="{ 'c-orange': $route.fullPath === '/register' }">免费注册</NuxtLink></div></li><li><div class=""><NuxtLinkto="/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=""><ahref="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的话,需要在浏览器渲染之后,在能获得cookiethis.showInfo()},methods: {//显示用户信息//1.从cookie中获取用户信息,如果不存在用户信息则用户未登录//2.存在用户信息则需要校验token是否为合法tokenshowInfo() {//判断coolie中有没有值let userinfo = cookie.get('userInfo')if (!userinfo) {console.log('coolie不存在')this.userInfo = nullreturn}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})},//退出,需要销毁cookielogout() {cookie.set('userInfo', '')window.location.href = '/login'},},
}
</script>

plugins\axios.js请求拦截器

$axios.onRequest((config) => {let userInfo = cookie.get('userInfo')if (userInfo) {// debuggeruserInfo = 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 {@Resourceprivate RedisTemplate redisTemplate;@Resourceprivate 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) {//获取请求头中的tokenString 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);}}}

④实现结果

在这里插入图片描述
在这里插入图片描述

未更新

未更新

未更新

未更新

未更新

未更新

未更新

未更新

查看全文

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.dgrt.cn/a/2311954.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章:

在这里插入图片描述

项目8:用户注册和登录的前后端联调

项目8:用户注册和登录的前后端联调
1.前端项目使用
2.前端项目注册模块
3.后端完成项目注册
4.前端项目登录模块
5.后端完成项目登录
6.用户认证(校验用户是否登录) 项目8:用户注册和登录的前后端联调
1.前端项目使用
直接……

《重构》:移除中间人(Remove Middle Man)/ 偶尔有用的迪米特建议

首先明确,Remove middle man 和 hide delegate 这两种重构技巧并不是对立面。 隐藏委托关系和移除中间人这两种重构技巧并不是相对立的,它们可以互相结合使用。 在某些情况下,使用隐藏委托关系能够将复杂度封装在一个类中,并提供一……

JavaScript事件机制详解

如何实现一个事件的发布订阅
可以通过以下步骤实现 JavaScript 中的发布-订阅模式:
创建一个事件管理器对象。
const eventManager {events: {},subscribe: function (eventName, callback) {if (!this.events[eventName]) {this.events[eventName] [];}this.e……

C++ 底层实现

文章目录STL库的底层实现arrayvectordequelistforward_listset、mapunordered_map、unordered_set迭代器STL库的底层实现 顺序容器 array数组vector向量deque双向队列list双向链表forward_list前向链表 关联容器 map 关联数组key value map、multimap 排序实现unordered_map、……

【chatgpt】AI写的排序算法,模仿notepad++“升序排列整数”

java 实现和notepad一样的,对多行的string文本进行“升序排列整数”的排序算法。 java 实现和notepad一样的,对多行的string文本进行“升序排列整数”的排序算法。 chatgpt省流版 : 不会 notepad 的 “行操作 – 升序排列整数”是怎么是实现的&#xff1f……

肖 sir_就业课__009接口测试和接口自动化讲解

接口测试和接口自动化讲解 一、你会接口测试吗? 会 二、你做过哪些接口? cms项目接口:查询接口,登录接口,添加用户接口,用户管理接口,文章管理接口,删除用户接口,删除栏目……

华为OD机试-对称美学-2022Q4 A卷-Py/Java/JS

对称就是最大的美学,现有一道关于对称字符串的美学。 已知: 第1个字符串:R
第2个字符串:BR
第3个字符串:RBBR 第4个字符串:BRRBRBBR 第5个字符串:RBBRBRRBBRRBRBBR相信你已经发现规律了,没错! 就是第i个字符串=第i-1号字符串的取反+第i-1号字符串。取反即(R->B,B……

mysql 主从同步案例集合

记一次 MySQL 主从同步异常的排查记录,百转千回! relay log 损坏了,导致从库的 SQL 线程解析 relay log 时出现异常。从库恢复方式是通过手动设置当时出错的 GTID 的下一个值,让从库不从主库同步这个 GTID,最后从库就能……

华为手表开发:WATCH 3 Pro(18)传感器订阅 方向传感器

华为手表开发:WATCH 3 Pro(18)传感器订阅 方向传感器初环境与设备方向传感器鸿蒙开发文件夹:文件新增展示的文本标记index.hmlindex.cssindex.js初
希望能写一些简单的教程和案例分享给需要的人
鸿蒙可穿戴开发
环境与设备
系……

JDBC-2.JDBC介绍

一.与数据库连接代码
//测试数据库是否成功连接的类
package code.util;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
public class JDBCUtil {public static void main(String[] args)throws Exception {//1.注册驱动Class.forN……

STM32F103基于HAL库I2C/SPI硬件接口+DMA驱动 SSD1306 Oled

STM32F103基于HAL库I2C/SPI硬件接口DMA驱动 SSD1306 Oled✨由于手上只有I2C接口的SSD1306 OLED屏幕,仅测试了硬件I2C驱动显示功能,实际测试的FPS帧率在37或38变化。 📢本项目从Github开源项目中移植过来,开源地址:http……

English Learning – L2 第 14 次小组纠音 复习元音 [ɔɪ ] [aʊ] [əʊ] [ɪə] 弱读 2023.4.12 周三

English Learning – L2 第 14 次小组纠音 复习元音 [ɔɪ ] [aʊ] [əʊ] [ɪə] 弱读 2023.4.12 周三共性问题coin voice /kɔɪn/ /vɔɪs/ 中 ɔɪvowel pounds /ˈvaʊəl/ /paʊndz/ 中的 aʊshow /ʃəʊ/beer nearly /bɪə/ /ˈnɪəlɪ/ 中的 ɪəbest bed ten /best/……

LC-1157. 子数组中占绝大多数的元素(二分查找+随即猜,摩尔投票+线段树,upper_bound和lower_bound函数)

文章目录[1157. 子数组中占绝大多数的元素](https://leetcode.cn/problems/online-majority-element-in-subarray/)统计每个元素的索引-超时二分查找 随机猜摩尔投票 线段树[剑指 Offer 39. 数组中出现次数超过一半的数字](https://leetcode.cn/problems/shu-zu-zhong-chu-xi……

杭州旭航集团,申请纳斯达克IPO上市,募资9800万美元

来源:猛兽财经 作者:猛兽财经 猛兽财经获悉,为中国企业提供数字内容营销服务的杭州旭航网络科技有限公司的控股公司Xuhang Holdings Ltd(以下简称:旭航集团),近期已向美国证券交易委员会(SEC)提……

深度学习快速参考:11~13

原文:Deep Learning Quick Reference 协议:CC BY-NC-SA 4.0 译者:飞龙 本文来自【ApacheCN 深度学习 译文集】,采用译后编辑(MTPE)流程来尽可能提升效率。 不要担心自己的形象,只关心如何实现目……

基于copula的风光联合场景生成与缩减

目录
1 主要内容
风光出力场景生成方法
2 部分程序
3 程序结果
4 程序链接
点击直达! 1 主要内容
该程序方法复现《融合风光出力场景生成的多能互补微网系统优化配置》风光出力场景生成部分,目前大多数研究的是不计风光出力之间的相关性影响&amp……

TS 使用自动提示生成对象中的键

TS 使用自动提示生成对象中的键
使用 plain object 而不是 class 的原因主要是在于 redux 中保存的对象需要为可序列化的对象。当然,具体操实现的时候肯定是可以无视这个需求的,不过 Redux 团队强烈建议使用可序列化对象,具体的官方文档说明……

Spring Boot概述(一)

1. SpringBoot 概述
1.1 SpringBoot 概念
SpringBoot提供了一种快速使用Spring的方式,基于约定优于配置的思想,可以让开发人员不必在配置与逻辑业务之间进行思维的切换,全身心的投入到逻辑业务的代码编写中,从而大大提高了开发的……

计算机笔试/面试常见逻辑题/智力题汇总

说明:按种类汇总,难度不分先后,做了分级罗列,方便后续扩充,大家有比较有意思的题目可以在讨论区讨论。 下面有的题题解相对复杂的直接参考了网上的一些解答,而有的题解我认为并不好的也做了补充&#xff0c……

OpenAI文档翻译——搭建第一个自己的ChatGPT应用

这篇主要是讲了重头到位创建一个基于OpenAI API的应用程序的过程,同时给出了Node.js、Python版本的实例代码。应用程序的构建总体来说是很简单的就是一个接口调用,前提是我们需要提供密匙。
如果想要获取更好的结果返回一个是可以给模型提供一些列子从而……

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注