B2C模式电商案例-用户注册与登录

 

1、用户注册-短信验证码

1.1分析

 

1.2后端

创建 SmsController类,调用阿里大鱼工具类,发送短信

 

package com.czxy.changgou4.controller;import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.czxy.changgou4.pojo.User;
import com.czxy.changgou4.utils.SmsUtil;
import com.czxy.changgou4.vo.BaseResult;
import org.apache.commons.lang.RandomStringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;@RestController
@RequestMapping("/sms")
public class SmsController {@Resourceprivate StringRedisTemplate redisTemplate;@PostMappingpublic BaseResult sendSms(@RequestBody User user){long start = System.currentTimeMillis();try {//发送短信//1 生产验证码String code = RandomStringUtils.randomNumeric(4);System.out.println("验证码:" + code);//2 并存放到reids中 , key: "sms_register" + 手机号 , value:验证码 , 1小时redisTemplate.opsForValue().set( "sms_register" + user.getMobile() , code , 1 , TimeUnit.HOURS);/**///3 发送短信SendSmsResponse smsResponse = SmsUtil.sendSms(user.getMobile(), user.getUsername() , code, "", "");//https://help.aliyun.com/document_detail/55284.html?spm=5176.doc55322.6.557.KvvIJxif("OK".equalsIgnoreCase(smsResponse.getCode())){return BaseResult.ok("发送成功");} else {return BaseResult.error(smsResponse.getMessage());}/*//模拟数据System.out.println("验证码:" + code);return BaseResult.ok("发送成功");*/} catch (Exception e) {long end = System.currentTimeMillis();System.out.println( end - start);return BaseResult.error("发送失败" );}}
}

1.3前端

步骤一:修改apiclient.js,发送短信ajax操作

//发短信

  sendSms : ( user )=> {

    return axios.post('/web-service/sms', user )

  }

步骤二:修改Register.vue页面,给“发送验证码”绑定点击事件 sendSmsFn

<button  @click.prevent="sendSmsFn" >

        发送验证码<span>5秒</span>

</button>

 

步骤三:修改Register.vue页面,编写sendSmsFn函数,建议采用  ajax..then()..catch 可以处理异常

sendSmsFn () {

      this.$request.sendSms( this.user )

      .then(( response )=>{

        //发送短信的提示信息

        this.userMsg.smsData = response.data

      })

      .catch(( error )=>{

        //错误提示信息

        alert( error.message )

      })

    }

步骤四:修改Register.vue页面,提供变量smsData

userMsg : { //错误提示数据

        usernameData : "",

        mobileData : "",

        smsData : ""

      }

步骤五:修改Register.vue页面,显示 smsData提示信息 

 <p :class="userMsg.smsData.code == 1 ? 'success' : 'error'">{{userMsg.smsData.message}}

</p>

1.4发送验证码倒计时

步骤一:提供3个变量,用于控制倒计时

 

步骤二:在标签上面控制倒计时的显示

<button :disabled="btnDisabled" @click.prevent="sendSmsFn" >

      发送验证码<span v-show="btnDisabled">{{seconds}}</span>

</button>

步骤三:发送短信后,开启倒计时控制

sendSmsFn () {this.$request.sendSms( this.user ).then(( response )=>{//发送短信的提示信息this.userMsg.smsData = response.data//按钮不可用this.btnDisabled = true;//倒计时this.timer = setInterval( ()=>{if(this.seconds <= 1){//结束// 重置秒数this.seconds = 5;// 按钮可用this.btnDisabled = false;// 停止定时器clearInterval(this.timer);} else {this.seconds --;}} , 1000);}).catch(( error )=>{//错误提示信息alert( error.message )})}

2、用户注册

2.1:后端

  1. 保存前需要再次进行服务端校验
    1. 用户名是否注册
    2. 手机号是否注册
    3. 验证码是否失效
    4. 验证码是否错误
  2. 密码需要使用 BCrypt进行加密

 步骤一:修改UserService接口,添加register方法

/**
 * 用户注册
 * @param user
 * @return
 */
public boolean register(User user) ;

步骤二:完善UserServiceImpl实现类

@Override
public boolean register(User user) {
    //密码加密
    String newPassword = BCrypt.hashpw(user.getPassword());
    user.setPassword(newPassword);

    //处理数据
    user.setCreatedAt(new Date());
    user.setUpdatedAt(user.getCreatedAt());

    int insert = baseMapper.insert(user);

    return insert == 1;
}

 步骤三:修改UserController,添加register方法

/**
 * 用户注册
 * @param user
 * @return
 */
@PostMapping("/register")
public BaseResult  register(@RequestBody User user){

    //服务端校验
    User findUser = userService.findByUsername(user.getUsername());
    if(findUser != null) {
        return BaseResult.error("用户名已经存在");
    }

    findUser = userService.findByMobile(user.getMobile());
    if(findUser != null) {
        return BaseResult.error("电话号码已经存在");
    }

    //验证码
    String code = stringRedisTemplate.opsForValue().get("sms_register" + user.getMobile());
    //删除redis中的验证码
    stringRedisTemplate.delete("sms_register" + user.getMobile());
    if(code == null) {
        return BaseResult.error("验证码失效");
    }
    if(!code.equals(user.getCode())) {
        return BaseResult.error("验证码不正确");
    }

    //注册
    boolean register = userService.register(user);

    if(register) {
        return BaseResult.ok("注册成功");
    }
    return BaseResult.error("注册失败");
}

2.2:日期处理

编写 DateMetaObjectHandler 用于处理“创建时间”和“修改日期”

package com.czxy.changgou4.handler;import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;import java.util.Date;@Component
public class DateMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {this.setFieldValByName("createdAt", new Date(), metaObject);this.setFieldValByName("updatedAt", new Date(), metaObject);}@Overridepublic void updateFill(MetaObject metaObject) {this.setFieldValByName("updatedAt", new Date(), metaObject);}
}

完善User JavaBean,设置填充方式

@TableField(value="created_at",fill = FieldFill.INSERT)
private Date createdAt;

@TableField(value="updated_at",fill = FieldFill.INSERT_UPDATE)
private Date updatedAt;

 

2.3:前端

步骤一:修改 api.js ,添加注册函数

//注册

  register : ( user )=> {

    return axios.post('/web-service/user/register', user )

  }        

步骤二:处理表单,验证码input绑定数据,提交按钮绑定事件

 

步骤三:完善data区域的user数据

 

步骤四:编写registerFn函数

async registerFn() {

      let { data } = await this.$request.register( this.user )

      if( data.code == 20000) {

        //成功

        this.$router.push('/login')

      } else {

        //失败--与发送验证码使用一个位置显示错误信息

        this.userMsg.smsData = data

      }

    }

 3、用户登录

3.1:构建页面:Login.vue

步骤一:创建Login.vue

 

步骤二:绘制通用模块

<template><div><TopNav></TopNav><div style="clear:both;"></div><HeaderLogo></HeaderLogo><div style="clear:both;"></div><!-- 正文 --><div style="clear:both;"></div><Footer></Footer></div>
</template><script>
import TopNav from '../components/TopNav'
import HeaderLogo from '../components/HeaderLogo'
import Footer from '../components/Footer'
export default {head: {title: '用户登录',link: [{rel:'stylesheet',href:'style/login.css'}],script: []},components : {TopNav,HeaderLogo,Footer},}
</script><style></style>

步骤三:绘制登录表单

<template><div><TopNav></TopNav><div style="clear:both;"></div><HeaderLogo></HeaderLogo><div style="clear:both;"></div><!-- 正文 --><!-- 登录主体部分start --><div class="login w990 bc mt10"><div class="login_hd"><h2>用户登录</h2><b></b></div><div class="login_bd"><div class="login_form fl"><form action="" method="post"><ul><li><label for="">用户名:</label><input type="text" class="txt" name="username" /></li><li><label for="">密码:</label><input type="password" class="txt" name="password" /><a href="">忘记密码?</a></li><li class="checkcode"><label for="">验证码:</label><input type="text"  name="checkcode" /><img src="images/checkcode1.jpg" alt="" /><span>看不清?<a href="">换一张</a></span></li><li><label for="">&nbsp;</label><input type="checkbox" class="chb"  /> 保存登录信息</li><li><label for="">&nbsp;</label><input type="submit" value="" class="login_btn" /></li></ul></form><div class="coagent mt15"><dl><dt>使用合作网站登录商城:</dt><dd class="qq"><a href=""><span></span>QQ</a></dd><dd class="weibo"><a href=""><span></span>新浪微博</a></dd><dd class="yi"><a href=""><span></span>网易</a></dd><dd class="renren"><a href=""><span></span>人人</a></dd><dd class="qihu"><a href=""><span></span>奇虎360</a></dd><dd class=""><a href=""><span></span>百度</a></dd><dd class="douban"><a href=""><span></span>豆瓣</a></dd></dl></div></div><div class="guide fl"><h3>还不是商城用户</h3><p>现在免费注册成为商城用户,便能立刻享受便宜又放心的购物乐趣,心动不如行动,赶紧加入吧!</p><a href="regist.html" class="reg_btn">免费注册 >></a></div></div></div><!-- 登录主体部分end --><div style="clear:both;"></div><Footer></Footer></div>
</template><script>
import TopNav from '../components/TopNav'
import HeaderLogo from '../components/HeaderLogo'
import Footer from '../components/Footer'
export default {head: {title: '用户登录',link: [{rel:'stylesheet',href:'style/login.css'}],script: []},components : {TopNav,HeaderLogo,Footer},}
</script><style></style>

3.2:分析

 

3.3:验证码:生成与显示

步骤一:后端生产验证码,并将用户保存Redis

1存放redis中验证码key格式:"login" + 用户名

生成验证码工具类:

package com.czxy.changgou4.controller;import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import java.util.concurrent.TimeUnit;@Controller
@RequestMapping("/verifycode")
public class VerifyCodeController {@Resourceprivate StringRedisTemplate stringRedisTemplate;@GetMappingpublic void verifyCode(String username , HttpServletResponse response ) throws IOException {//字体只显示大写,去掉了1,0,i,o几个容易混淆的字符String VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";int IMG_WIDTH = 72;int IMG_HEIGTH = 27;Random random = new Random();//创建图片BufferedImage image = new BufferedImage(IMG_WIDTH, IMG_HEIGTH, BufferedImage.TYPE_INT_RGB);//画板Graphics g = image.getGraphics();//填充背景g.setColor(Color.WHITE);g.fillRect(1,1,IMG_WIDTH-2,IMG_HEIGTH-2);g.setFont(new Font("楷体", Font.BOLD,25));StringBuilder sb = new StringBuilder();//写字for(int i = 1 ; i <= 4 ; i ++){//随机颜色g.setColor(new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255)));int len = random.nextInt(VERIFY_CODES.length());String str = VERIFY_CODES.substring(len,len+1);sb.append(str);g.drawString(str, IMG_WIDTH / 6 * i , 22 );}//将验证码存放到redisstringRedisTemplate.opsForValue().set( "login" + username , sb.toString() , 1 , TimeUnit.HOURS);// 生成随机干扰线for (int i = 0; i < 30; i++) {//随机颜色g.setColor(new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255)));int x = random.nextInt(IMG_WIDTH - 1);int y = random.nextInt(IMG_HEIGTH - 1);int x1 = random.nextInt(12) + 1;int y1 = random.nextInt(6) + 1;g.drawLine(x, y, x - x1, y - y1);}//响应到浏览器ImageIO.write(image,"jpeg", response.getOutputStream());}
}

步骤二:点击“换一张”显示验证码

默认不显示验证码

点击“换一张”获得验证码 

methods: {

changeVerifyCode() {

// console.info(this)

this.imgSrc = `${this.$axios.defaults.baseURL}/web-service/verifycode?username=${this.user.username}&t=${new Date().getTime()}`

},

}

 

 

3.4:通过用户名查询:实现

修改UserController,添加 findByUsername函数

/**
 * 通过用户名查询
 * @param user
 * @return 返回用户对象
 */
@PostMapping("/findByUsername")
public User findByUsername(@RequestBody User user){
    //查询用户
    User findUser = userService.findByUsername( user.getUsername() );
    return findUser;
}

3.5:认证服务:构建项目(changgou4-service-auth)

创建项目

导入pom文件

<dependencies><!--自定义项目--><dependency><groupId>com.czxy.changgou</groupId><artifactId>changgou4_common_auth</artifactId></dependency><!--web起步依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- nacos 客户端 --><dependency><groupId>com.alibaba.nacos</groupId><artifactId>nacos-client</artifactId></dependency><!-- nacos 服务发现 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId></dependency><!--swagger2--><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId></dependency><!--    feign    --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>
</dependencies>

创建yml文件

server:
  port: 8085
spring:
  application:
    name: auth-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848   #nacos服务地址

sc:
  jwt:
    secret: sc@Login(Auth}*^31)&czxy% # 登录校验的密钥
    pubKeyPath: D:/rsa/rsa.pub # 公钥地址
    priKeyPath: D:/rsa/rsa.pri # 私钥地址
    expire: 360 # 过期时间,单位分钟

 

配置启动类

package com.czxy.changgou4;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class CGAuthServiceApplication {public static void main(String[] args) {SpringApplication.run(CGAuthServiceApplication.class, args);}
}

 配置类

 

3.6:认证服务:用户登录后端

步骤一:创建AuthUser 封装对象(与User比较,缺数据库相关注解)

package com.czxy.changgou4.domain;import lombok.Data;import java.util.Date;@Data
public class AuthUser {private Long id;private String username;private String password;private String face;private Integer expriece;private String email;private String mobile;private Date createdAt;private Date updatedAt;private String code;private String password_confirm;
}

步骤二:创建UserFeign,完成远程用户查询功能

package com.czxy.changgou4.feign;import com.czxy.changgou4.domain.AuthUser;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;@FeignClient(value = "web-service",path="/user")
public interface UserFeign {@PostMapping("/findByUsername")public AuthUser findByUsername(@RequestBody AuthUser user);}

步骤三:创建AuthService接口,编写登录方法 login()

package com.czxy.changgou4.service;import com.czxy.changgou4.domain.AuthUser;public interface AuthService {/*** 用户登录* @param user* @return*/public AuthUser login(AuthUser user ) ;
}

步骤四:创建AuthService实现类,并通过BCrypt校验密码

package com.czxy.changgou4.service.impl;import com.czxy.changgou4.domain.AuthUser;
import com.czxy.changgou4.feign.UserFeign;
import com.czxy.changgou4.service.AuthService;
import com.czxy.changgou4.utils.BCrypt;
import org.springframework.stereotype.Service;import javax.annotation.Resource;@Service
public class AuthServiceImpl implements AuthService {@Resourceprivate UserFeign userFeign;/*** 用户登录* @param user* @return*/public AuthUser login(AuthUser user ) {//远程查询用户AuthUser findUser = userFeign.findByUsername(user);if(findUser == null) {return null;}//校验密码是否正确boolean checkpw = BCrypt.checkpw( user.getPassword(), findUser.getPassword());if(checkpw){return findUser;}return null;}}

步骤五:创建AuthController,添加login方法

        redis中登录验证码和用户输入的验证码进行匹配 

package com.czxy.changgou4.controller;import com.czxy.changgou4.domain.AuthUser;
import com.czxy.changgou4.service.AuthService;
import com.czxy.changgou4.vo.BaseResult;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestController
@RequestMapping("/auth")
public class AuthController {@Resourceprivate AuthService authService;@Resourceprivate StringRedisTemplate stringRedisTemplate;@PostMapping("/login")public BaseResult login(@RequestBody AuthUser authUser) {if (authUser.getUsername() == null) {return BaseResult.error("请输入用户名");}if (authUser.getPassword() == null) {return BaseResult.error("请输入密码");}if (authUser.getCode() == null) {return BaseResult.error("请输入验证码");}//校验验证码,使用后删除String redisCode = stringRedisTemplate.opsForValue().get("login" + authUser.getUsername());//删除验证码,保证验证码的一次性stringRedisTemplate.delete("login" + authUser.getUsername());if (redisCode == null) {return BaseResult.error("无效验证码");}if (!redisCode.equalsIgnoreCase(authUser.getCode())) {return BaseResult.error("验证码错误");}AuthUser loginAuthUser = authService.login(authUser);if (loginAuthUser != null) {return BaseResult.ok("登录成功").append("loginAuthUser",loginAuthUser);}return BaseResult.error("用户名或密码错误");}
}

3.7:认证服务:用户登录前端

步骤一:修改apiclient.js,添加login函数

//登录

  login : ( user )=> {

    return axios.post('/auth-service/auth/login', user )

  }

步骤二:修改Login.vue,给验证码绑定变量

 

步骤三:修改Login.vue,给提交按钮绑定事件

 

步骤四:编写loginFn完成登录功能

async loginFn() {

let { data:baseResult } = await this.$request.login(this.user)

if(baseResult.code == 20000) {

sessionStorage.setItem('user',JSON.stringify(loginUser))

//跳转到首页

location.href = '/'

} else {

//失败给出提示

this.errorMsg = baseResult.message

}

},

步骤五:创建首页 ~/pages/index.vue

<template><div><TopNav></TopNav></div>
</template><script>
import TopNav from '../components/TopNav'export default {head: {title: '首页',link: [{rel:'stylesheet',href:'style/index.css'},{rel:'stylesheet',href:'style/bottomnav.css'}],script: [{ type: 'text/javascript', src: 'js/header.js' },{ type: 'text/javascript', src: 'js/index.js' },]},components : {TopNav,},
}
</script><style></style>

3.8:修改 TopNav.vue 组件

分析:完善导航条,根据vuex中的数据,显示不同内容

步骤一:创建 ~/store/index.js ,并编写vuex内容 

export const state = () => ({

  user: null

})

//通用设置

export const mutations = {

  setData( state , obj) {

    state[obj.key] = obj.value

  }

}

步骤二:页面登录成功,将用户信息保存到vuex中

// 将用户信息保存到vuex中

this.$store.commit('setData', {key:'user',value: data.data })

 

步骤三:修改顶部导航TopNav.vue

<template><!-- 顶部导航 start --><div class="topnav"><div class="topnav_bd w990 bc"><div class="topnav_left"></div><div class="topnav_right fr"><ul><li v-if="user != null">您好,{{user.username}} 欢迎来到畅购! <a href="" @click.prevent="logout">退出</a></li><li v-if="user != null" class="line">|</li><li v-if="user == null">[<a href="/login">登录</a>] [<a href="/register">免费注册</a>] </li><li v-if="user == null" class="line">|</li><li v-if="user != null">我的订单</li><li v-if="user != null" class="line">|</li><li>客户服务</li></ul></div></div></div><!-- 顶部导航 end -->
</template><script>
import {mapState,mapMutations} from 'vuex'
export default {mounted() {let userStr = sessionStorage.getItem('user')if(userStr){// 将string数据转换object,并填充到vuex中this.setData({key:'user',value: JSON.parse(userStr)})}},methods: {logout() {sessionStorage.removeItem('user')sessionStorage.removeItem('token')//设置vuex中的数据为空this.setData({key:'user',value: null })this.$router.push('/login')},...mapMutations(['setData'])},computed: {...mapState(['user'])},
}
</script><style></style>

3.9:vuex刷新数据丢失

  • 刷新操作:
    • 点击刷新按钮
    • 点击回退按钮
    • 地址栏直接输入地址
  • 现象:
    • vuex在刷新操作后,数据丢失了
  • 解决方案
    • 方案1:不是公共组件:页面在pages目录下,可以nuxt.js提供 fetch进行操作。
    • 方案2:是公共组件:组件在components目录下,借助第三方进行存储(cookie、localStorage、sessionStorage)
      • 选择1:sessionStorage存放数据,如果vuex中没有,将sessionStorage同步过去。
      • 选择2:vuex中actions模块就可以发送ajax,从而同步数据。
  • 具体操作:
    • 如果vuex中没有数据,使用sessionStorage的数据填充vuex。
    • 修改TopNav.vue页面
<template><!-- 顶部导航 start --><div class="topnav"><div class="topnav_bd w990 bc"><div class="topnav_left"></div><div class="topnav_right fr"><ul><li v-if="user!=null">您好,{{user.username}}欢迎来到畅购!</li><li v-if="user==null">[<nuxt-link to="/Login">登录</nuxt-link>]</li><li v-if="user==null">[<nuxt-link to="/Register">免费注册</nuxt-link>]</li><li v-if="user!=null">[<nuxt-link to="/Login">退出</nuxt-link>]</li><li v-if="user!=null" class="line">|</li><li v-if="user!=null">我的订单</li><li class="line">|</li><li>客户服务</li></ul></div></div></div><!-- 顶部导航 end -->
</template><script>
import {mapState, mapMutations} from 'vuex'
export default {computed: { //计算属性整合vuex// user() {//   return this.$store.state.user// }...mapState(['user'])},mounted() {// 从sessionStorage获得信息,如果存在,直接添加vuex中let userStr = sessionStorage.getItem('user')if(userStr) {let user = JSON.parse(userStr)//将数据存放到vuex中this.setData({key:'user',value: user })}},methods: {...mapMutations(['setData']),},
}
</script><style></style>

 4、整合JWT

4.1整合分析

  • 生成token:在用户登录成功,根据用户的登录信息,生成登录标识token,并返回给浏览器。
  • 使用token:完善ajax请求,在请求之前添加请求头,设置token
  • 校验token:在网关中编写过滤器,进行请求进行拦截,并校验token。
    • 白名单:在白名单中的请求,是不需要token可以直接访问的。

 4.2生成Token

        用户登录成功,生成token,并将token响应给浏览器。(认证服务 AuthService)

步骤一:查看 application.yml文件,确定 jwt配置信息

 步骤二:创建JwtProperties文件,用于加载sc.jwt配置信息

 

package com.czxy.changgou4.config;import com.czxy.changgou4.utils.RsaUtils;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import java.io.File;
import java.security.PrivateKey;
import java.security.PublicKey;@Data
@ConfigurationProperties(prefix = "sc.jwt")
@Component
public class JwtProperties {private String secret; // 密钥private String pubKeyPath;// 公钥private String priKeyPath;// 私钥private int expire;// token过期时间private PublicKey publicKey; // 公钥private PrivateKey privateKey; // 私钥private static final Logger logger = LoggerFactory.getLogger(JwtProperties.class);@PostConstructpublic void init(){try {File pubFile = new File(this.pubKeyPath);File priFile = new File(this.priKeyPath);if( !pubFile.exists() || !priFile.exists()){RsaUtils.generateKey( this.pubKeyPath ,this.priKeyPath , this.secret);}this.publicKey = RsaUtils.getPublicKey( this.pubKeyPath );this.privateKey = RsaUtils.getPrivateKey( this.priKeyPath );} catch (Exception e) {throw new RuntimeException(e.getMessage());}}

步骤三:修改AuthController,注入JwtProperties,并使用JwtUtils生成token

 

 

 

package com.czxy.changgou4.controller;import com.czxy.changgou4.config.JwtProperties;
import com.czxy.changgou4.domain.AuthUser;
import com.czxy.changgou4.service.AuthService;
import com.czxy.changgou4.utils.JwtUtils;
import com.czxy.changgou4.vo.BaseResult;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestController
@RequestMapping("/auth")
public class AuthController {@Resourceprivate AuthService authService;@Resourceprivate StringRedisTemplate stringRedisTemplate;@Resourceprivate JwtProperties jwtProperties;@PostMapping("/login")public BaseResult login(@RequestBody AuthUser authUser) {if (authUser.getUsername() == null) {return BaseResult.error("请输入用户名");}if (authUser.getPassword() == null) {return BaseResult.error("请输入密码");}if (authUser.getCode() == null) {return BaseResult.error("请输入验证码");}//校验验证码,使用后删除String redisCode = stringRedisTemplate.opsForValue().get("login" + authUser.getUsername());//删除验证码,保证验证码的一次性stringRedisTemplate.delete("login" + authUser.getUsername());if (redisCode == null) {return BaseResult.error("无效验证码");}if (!redisCode.equalsIgnoreCase(authUser.getCode())) {return BaseResult.error("验证码错误");}AuthUser loginAuthUser = authService.login(authUser);if (loginAuthUser != null) {String token = JwtUtils.generateToken(loginAuthUser, jwtProperties.getExpire(), jwtProperties.getPrivateKey());return BaseResult.ok("登录成功").append("loginAuthUser",loginAuthUser).append("token",token);}return BaseResult.error("用户名或密码错误");}
}

4.3使用Token

步骤一:登录成功后保存token,修改 Login.vue页面

 

async loginFn() {

let { data:baseResult } = await this.$request.login(this.user)

if(baseResult.code == 20000) {

let loginUser = baseResult.other.loginAuthUser

sessionStorage.setItem('loginUser',JSON.stringify(loginUser))

//保存token

sessionStorage.setItem('token',baseResult.other.token)

//跳转到首页

location.href = '/'

} else {

//失败给出提示

this.errorMsg = baseResult.message

this.changeVerifyCode()

}

},

步骤二:请求是自动携带token,修改apiclient.js,将token添加到请求头 

 

//参考 https://axios.nuxtjs.org/helpers// 增强$axios,每一个请求都携带tokenlet token = sessionStorage.getItem('token')if(token) {// 添加一个固定的请求头 Adds header:`Authorizatino: 123` to all requests$axios.setToken(token)}

步骤三:检查 nuxt.conf.js,插件模式改成“client”

否则抛异常“sessionStorage is not defined” 

plugins: [

    { src: '~plugins/apiclient.js', mode: 'client' }

  ],

4.4校验token(在网关项目处完成)

步骤一:修改application.yml添加jwt配置

#自定义内容
sc:
  jwt:
    secret: sc@Login(Auth}*^31)&czxy% # 登录校验的密钥
    pubKeyPath: D:/rsa/rsa.pub # 公钥地址
    priKeyPath: D:/rsa/rsa.pri # 私钥地址
    expire: 360 # 过期时间,单位分钟

 

步骤二:创建 JwtProperties,用于加载配置文件

 

package com.czxy.changgou4.config;import com.czxy.changgou4.utils.RsaUtils;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import java.io.File;
import java.security.PrivateKey;
import java.security.PublicKey;@Data
@ConfigurationProperties(prefix = "sc.jwt")
public class JwtProperties {private String secret; // 密钥private String pubKeyPath;// 公钥private String priKeyPath;// 私钥private int expire;// token过期时间private PublicKey publicKey; // 公钥private PrivateKey privateKey; // 私钥private static final Logger logger = LoggerFactory.getLogger(JwtProperties.class);@PostConstructpublic void init(){try {File pubFile = new File(this.pubKeyPath);File priFile = new File(this.priKeyPath);if( !pubFile.exists() || !priFile.exists()){RsaUtils.generateKey( this.pubKeyPath ,this.priKeyPath , this.secret);}this.publicKey = RsaUtils.getPublicKey( this.pubKeyPath );this.privateKey = RsaUtils.getPrivateKey( this.priKeyPath );} catch (Exception e) {throw new RuntimeException(e.getMessage());}}}

 步骤三:编写过滤器,对所有路径进行拦截

package com.czxy.changgou4.filter;import com.czxy.changgou4.config.FilterProperties;
import com.czxy.changgou4.config.JwtProperties;
import com.czxy.changgou4.pojo.User;
import com.czxy.changgou4.utils.JwtUtils;
import com.czxy.changgou4.utils.RsaUtils;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;@Component
public class LoginFilter implements GlobalFilter, Ordered {@Resourceprivate JwtProperties jwtProperties;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//1 获得请求路径ServerHttpRequest request = exchange.getRequest();String path = request.getURI().getPath();System.out.println(path);//2 白名单放行//3 获得tokenString token = request.getHeaders().getFirst("Authorization");//4 校验tokentry {JwtUtils.getObjectFromToken(token, RsaUtils.getPublicKey(jwtProperties.getPubKeyPath()), User.class);return chain.filter(exchange);} catch (Exception e) {ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.UNAUTHORIZED);response.getHeaders().add("Content-Type","application/json;charset=UTF-8");DataBuffer wrap = response.bufferFactory().wrap("没有权限".getBytes(StandardCharsets.UTF_8));return exchange.getResponse().writeWith(Flux.just(wrap));}}@Overridepublic int getOrder() {return 1;}
}

 步骤四:修改前端 apiclient.js 文件,用于处理401异常

 

//异常处理:401

$axios.onError(error => {

if(error.response.status === 401) {

//重定向

redirect('/Login')

}

})

api.js 完整代码

var axios = null

export default ({ $axios, redirect }, inject) => {

//参考 https://axios.nuxtjs.org/helpers

// 增强$axios,每一个请求都携带token

let token = sessionStorage.getItem('token')

if(token) {

// 添加一个固定的请求头 Adds header:`Authorizatino: 123` to all requests

$axios.setToken(token)

}

//异常处理:401

$axios.onError(error => {

if(error.response.status === 401) {

//重定向

redirect('/Login')

}

})

//赋值

axios = $axios

//4) 将自定义函数交于nuxt

// 使用方式1:在vue中,this.$request.xxx()

// 使用方式2:在nuxt的asyncData中,content.app.$request.xxx()

inject('request', request)

}

 

4.5白名单

不需要拦截的资源都配置到yml文件中,在过滤器直接放行

步骤一:修改application.yml文件

 

#自定义内容
sc:jwt:secret: sc@Login(Auth}*^31)&czxy% # 登录校验的密钥pubKeyPath: D:/rsa/rsa.pub # 公钥地址priKeyPath: D:/rsa/rsa.pri # 私钥地址expire: 360 # 过期时间,单位分钟filter:allowPaths:- /checkusername- /checkmobile- /sms- /register- /login- /verifycode- /categorys- /news- /brands- /specifications- /search- /goods- /comments- swagger- /api-docs

步骤二:创建FilterProperties配置文件,用于存放允许放行的路径

 

 

package com.czxy.changgou4.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.List;

 
@Data
@ConfigurationProperties(prefix="sc.filter")
public class FilterProperties {

    //允许访问路径集合
    private List<String> allowPaths;

 

步骤三:修改 LoginFilter,放行名单中配置的路径 

 

 

 

package com.czxy.changgou4.filter;import com.czxy.changgou4.config.FilterProperties;
import com.czxy.changgou4.config.JwtProperties;
import com.czxy.changgou4.pojo.User;
import com.czxy.changgou4.utils.JwtUtils;
import com.czxy.changgou4.utils.RsaUtils;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;@Component
//2.1 加载JWT配置类
@EnableConfigurationProperties({FilterProperties.class} )       //加载配置类
public class LoginFilter implements GlobalFilter, Ordered {@Resourceprivate FilterProperties filterProperties;@Resourceprivate JwtProperties jwtProperties;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//1 获得请求路径ServerHttpRequest request = exchange.getRequest();String path = request.getURI().getPath();System.out.println(path);//2 白名单放行for (String allowPath  : filterProperties.getAllowPaths()) {//判断包含if(path.contains(allowPath)){return chain.filter(exchange);}}//3 获得tokenString token = request.getHeaders().getFirst("Authorization");//4 校验tokentry {JwtUtils.getObjectFromToken(token, RsaUtils.getPublicKey(jwtProperties.getPubKeyPath()), User.class);return chain.filter(exchange);} catch (Exception e) {ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.UNAUTHORIZED);response.getHeaders().add("Content-Type","application/json;charset=UTF-8");DataBuffer wrap = response.bufferFactory().wrap("没有权限".getBytes(StandardCharsets.UTF_8));return exchange.getResponse().writeWith(Flux.just(wrap));}}@Overridepublic int getOrder() {return 1;}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用户注册登录退出功能的实现——淘淘商城(二十五)

静态资源配置用户注册页面 需求 分析总结 Controller层 用户登录页面 需求 分析总结 Controller层 门户工程整合单点登录系统 cookie的读写 需求开发 注册、登录、退出页面的跳转 需求开发 拦截器强制登录 需求Service层拦截器 运行工程 静态资源配置 taotao-sso的WEB-INF下…

JAVA实验:完成一个电商购物平台的登录注册窗体,点击“点我注册”,显示注册窗体,用户输入用户信息,点击提交后到达显示用户信息的窗体。

内容3&#xff1a;在内容2的基础上&#xff0c;用户点击登录注册窗体的登录按钮&#xff0c;到达商品信息查询的窗体&#xff0c;窗体如图3所示。 本次实验主要利用java类swing中的各种方法。 实验思路&#xff1a;通过实验所给出的窗口&#xff0c;使用swing中方法进行构建&a…

【Go实战 | 电商平台】(4) 用户注册

文章目录 1. 编写路由2. 注册路由3. 注册接口3.1 service层3.2 api层3.2 serializer 4. 注册服务 用到的包 github.com/gin-contrib/sessions github.com/gin-contrib/sessions/cookie github.com/gin-gonic/gin 1. 编写路由 新建一个WSGI应用程序实例。 r : gin.Default(…

Vue电商项目--登录与注册

登录注册静态组件 刚刚报了一个错误&#xff0c;找不到图片的资源 assets文件夹--放置全部组件共用静态资源 在样式当中也可以使用符号【src别名】。切记在前面加上 注册业务上 先修改原先的接口成这个按钮 然后把input框里面的数据保存到data中 注册业务下 就是点击获…

C++面经

编译、链接与库编译单文件编译多文件编译 动态链接与静态链接静态链接动态链接 面向对象c⾯向对象 三大特性封装继承多态静态多态&#xff08;模板或重载&#xff09;动态多态&#xff08;面向对象、继承、多态、虚函数&#xff09; 面向对象和面向过程语言的区别面向过程面向对…

GPT-4,究竟强大在哪里?

2023年3月15日&#xff0c;OpenAI向全世界进行了GPT-4的发布会&#xff0c;整个世界都为之沸腾了。GPT-4究竟是有多厉害&#xff1f;为什么能造成这么大的影响&#xff1f;今天小灰来仔细讲解一下。 基础配置 先说说基础配置&#xff0c;之前小灰分享过&#xff0c;GPT-3.5有17…

【EasyPoi实战系列】Spring Boot集成EasyPoi - 第467篇

历史文章&#xff08;文章累计460&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 《国内最全的Spring Boot系列之六》 6…

解密Prompt系列1. Tunning-Free Prompt:GPT2 GPT3 LAMA AutoPrompt

借着ChatGPT的东风&#xff0c;我们来梳理下prompt范式的相关模型。本系列会以A Systematic Survey of Prompting Methods in Natural Language Processing这篇综述为基础&#xff0c;分门别类的整理下这几年比较有代表性的prompt模型。或许你还以其他形式看到过prompt概念&…

未来式人工智能教育在国内的现状

无论是高空户外还是深海水下&#xff0c;或者火灾、地震现场&#xff0c;危险环境让身处其中的工作人员面临巨大威胁。而正在兴起的特种机器人&#xff0c;有望代替人类&#xff0c;在危险现场完成任务。格物斯坦表示&#xff1a;特种机器人&#xff0c;是机器人的一个重要分支…

chatgpt赋能python:Python如何隐藏进程

Python如何隐藏进程 介绍 进程是指运行中的程序在操作系统中的一个实例。在计算机系统中&#xff0c;进程通常都可以被用户或者其他程序所看到。然而&#xff0c;有时候我们需要隐藏进程&#xff0c;比如保护敏感信息或者防止恶意攻击。 Python是一种高级编程语言&#xff0…

UP主高薪裸辞转型独立开发者;LLM权威学习路线图;游戏开发最全AI工具盘点;LOGO制作保姆教程 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; Hugging Face 全球开源AI游戏开发挑战赛&#xff0c;7月8日开始 Open Source AI Game Jam 是 Hugging Face 举办的首场开源游戏开发挑…

ChatGPT到底是什么,带领大家详细解读!

前言 ChatGPT的强大程度相信很多同学都已经体验过了&#xff0c;无论是编写文章&#xff0c;问答&#xff0c;写代码展现除了非常惊艳的能力。一定喜欢技术的同学一定好奇&#xff0c;ChatGPT是如何做到“全知全能”&#xff0c;即会写文章&#xff0c;又会表达总结&#xff0…

IDE + ChatGPT,这款编辑器真的做到可以自动写代码了!

介绍 Cursor 是集成了 GPT-4 的 IDE 工具&#xff0c;目前免费并且无需 API Key&#xff0c;支持 Win、Mac、Linux 平台&#xff0c;可以按要求生成代码&#xff0c;或者让 AI 帮助优化代码&#xff0c;分析代码。Cursor目前已经集成了openai的GPT-4&#xff0c;它或将彻底改变…

CSDN周赛第30期题目解析(天然气定单、小艺读书、买苹果、圆桌)

CSDN周赛第30期&#xff0c;我应试成绩“0”分。试着对天然气定单、小艺读书、买苹果&#x1f34e;、圆桌四个题目&#xff0c;完成算法解析。 (本文获得CSDN质量评分【91】) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖…

猿人学逆向比赛第四题-gRPC题解 | Go版本

大家好&#xff0c;我是TheWeiJun&#xff0c;欢迎来到我的公众号。在现代互联网中&#xff0c;某些网站、App会使用gRPC进行数据传输&#xff0c;以加强数据的安全性和保密性。然而&#xff0c;逆向加密算法并不是一件简单的事情。本文将探讨如何逆向猿人学App的gRPC协议&…

ChatGPT 扩展来自动化你的生活

除非您一直生活在岩石下&#xff0c;否则您可能知道 ChatGPT 如何改变业务以及我们的工作和沟通方式。您只需访OpenAI 的网站即可解锁 ChatGPT 的强大功能。 但是&#xff0c;如果我们不仅可以在其官方网站上使用 ChatGPT&#xff0c;还可以在 微信、Excel、Word 以及更多地方使…

chatGPT的到来,是否意味新时代的来临

chatGPT是一种人工智能技术驱动的自然语言处理工具&#xff0c;是一种基于语言对话场景的语言模型&#xff0c;它使用了Transformer神经网络架构&#xff0c;也是GPT-3.5架构&#xff0c;功能具备上知天文下知地理&#xff0c;还可以进行撰写邮件、视频脚本、文案、翻译、代码等…

windows的软件能在鸿蒙系统运行吗,如果鸿蒙系统能不能兼容windows的所有应用软件,会让你惊喜吗...

如果华为能够让鸿蒙系统兼容Windows系统上所有的应用&#xff0c;那华为鸿蒙系统可能已经超脱时代了。 你得知道在两个不同平台&#xff0c;硬件和内核不同的情况下&#xff0c;华为如果想兼容所有的平台&#xff0c;可能所花费的人力&#xff0c;财力会更巨大。我们知道鸿蒙系…

为什么鸿蒙内核是安卓,鸿蒙系统发布,为什么有人说其为安卓换壳?

2021年4月&#xff0c;鸿蒙系统内测版正式面向消费者开放&#xff0c;而我由于在校读书也变没有时间去专门去进行鸿蒙系统的深度体验只是回来后简单的体验一下 首先我要说的是-鸿蒙它……不是安卓套壳&#xff01;&#xff01;&#xff01;我之前在评论区经常看见有人说鸿蒙就是…

华为p10 鸿蒙,全面上线!华为鸿蒙新消息传来,这是要彻底替换安卓

自从手机出现后&#xff0c;手机操作系统就掌握在少数几个厂商手中&#xff0c;在功能机时代&#xff0c;诺基亚几乎掌控了塞班系统&#xff0c;而微则掌握Windows mobie系统。 苹果开启智能手机时代后&#xff0c;手机系统主要被苹果和谷歌掌控。其中&#xff0c;苹果的IOS系统…