前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【畅购商城】用户注册以及整合JWT

【畅购商城】用户注册以及整合JWT

作者头像
陶然同学
发布2023-02-24 13:54:24
3670
发布2023-02-24 13:54:24
举报
文章被收录于专栏:陶然同学博客
    1. 用户注册
      1. 接口

POST http://localhost:10010/web-service/user/register

{

"mobile":"13612345677",

"password":"1234",

"username":"jack3",

"code":"3919"

}

      1. 后端
  1. 保存前需要再次进行服务端校验
    1. 用户名是否注册
    2. 手机号是否注册
    3. 验证码是否失效
    4. 验证码是否错误
  2. 密码需要使用 BCrypt进行加密
  3. 步骤一:修改UserService接口,添加register方法

/**

 * 用户注册

* @param user

* @return

*/

public boolean register(User user) ;

  1. 步骤二:完善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;

}

  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("注册失败");

}

      1. 日期处理(可选)
  1. 编写 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;

/**

 * @author 桐叔

* @email liangtong@itcast.cn

 */

@Component

public class DateMetaObjectHandler implements MetaObjectHandler {

@Override

public void insertFill(MetaObject metaObject) {

this.setFieldValByName("createdAt", new Date(), metaObject);

this.setFieldValByName("updatedAt", new Date(), metaObject);

    }

@Override

public void updateFill(MetaObject metaObject) {

this.setFieldValByName("updatedAt", new Date(), metaObject);

    }

}

  1. 完善User JavaBean,设置填充方式

@TableField(value="created_at",fill = FieldFill.INSERT)

private Date createdAt;

@TableField(value="updated_at",fill = FieldFill.INSERT_UPDATE)

private Date updatedAt;

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

//注册

  register : ( user )=> {

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

  }

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

<li class="checkcode">

<label for="">验证码:</label>

<input type="text" name="checkcode" v-model="user.code" />

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

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

</button>

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

</li>

<li>

<label for=""> </label>

<input type="checkbox" class="chb" checked="checked" /> 我已阅读并同意《用户注册协议》

</li>

<li>

<label for=""> </label>

<input type="submit" value="" @click.prevent="registerFn" class="login_btn" />

</li>

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

      user : {  //表单封装数据

        username : "",          //用户名

        mobile : "13699282444", //手机号

        password : "",          //密码

        code : "" //验证码

      },

  1. 步骤四:编写registerFn函数

async registerFn() {

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

if( data.code == 20000) {

//成功

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

      } else {

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

this.userMsg.smsData = data

      }

    }

    1. 整合JWT
      1. 整合分析
  1. 生成token:在用户登录成功,根据用户的登录信息,生成登录标识token,并返回给浏览器。
  2. 使用token:完善ajax请求,在请求之前添加请求头,设置token
  3. 校验token:在网关中编写过滤器,进行请求进行拦截,并校验token。
  4. 白名单:在白名单中的请求,是不需要token可以直接访问的。
      1. 生成Token
  5. 用户登录成功,生成token,并将token响应给浏览器。(认证服务 AuthService)
  6. 步骤一:查看 application.yml文件,确定 jwt配置信息
  1. 步骤二:创建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;

/**

 * @author 桐叔

* @email liangtong@itcast.cn

 */

@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);

@PostConstruct

public 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());

        }

    }

}

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

package com.czxy.changgou4.controller;

/**

 * @author 桐叔

* @email liangtong@itcast.cn

 */

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;

/**

 * Created by liangtong.

 */

@RestController

@RequestMapping("/auth")

public class AuthController {

@Resource

private AuthService authService;

@Resource

private StringRedisTemplate stringRedisTemplate;

@Resource

private JwtProperties jwtProperties;

@PostMapping("/login")

public BaseResult login(@RequestBody AuthUser user){

//校验验证码--使用后删除

String redisCode = stringRedisTemplate.opsForValue().get( "login" + user.getUsername() );

stringRedisTemplate.delete( "login" + user.getUsername() );

if(redisCode == null) {

return BaseResult.error("验证码无效");

        }

if(! redisCode.equalsIgnoreCase(user.getCode())) {

return BaseResult.error("验证码错误");

        }

//登录

AuthUser loginUser = authService.login(user);

if(loginUser != null ) {

//生成Token

String token = JwtUtils.generateToken(loginUser, jwtProperties.getExpire(), jwtProperties.getPrivateKey());

return BaseResult.ok("登录成功").append("loginUser",loginUser).append("token", token);

        } else {

return BaseResult.error("用户名或密码不匹配");

        }

    }

}

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

async loginFn() {

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

if( data.code == 20000) {

//成功

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

//保存token

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

//跳转到首页

this.$router.push('/')

      } else {

this.errorMsg = data.message

      }

    }

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

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

let token = sessionStorage.getItem('token')

if( token ) {

// Adds header: `Authorization: 123` to all requests

// this.$axios.setToken('123')

    $axios.setToken( token )

  }

  1. 步骤三:检查 nuxt.conf.js,插件模式改成“client”
    1. 否则抛异常“sessionStorage is not defined”

  plugins: [

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

  ],

      1. 校验token
  1. token的校验在网关项目处完成
  2. 步骤一:修改application.yml添加jwt配置

#自定义内容

sc:

  jwt:

    secret: sc@Login(Auth}*^31)&czxy% # 登录校验的密钥

pubKeyPath: D:/rsa/rsa.pub # 公钥地址

priKeyPath: D:/rsa/rsa.pri # 私钥地址

expire: 360 # 过期时间,单位分钟

  1. 步骤二:创建 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;

/**

 * @author 桐叔

* @email liangtong@itcast.cn

 */

@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);

@PostConstruct

public 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());

        }

    }

}

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

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;

/**

 * @author 桐叔

* @email liangtong@itcast.cn

 */

@Component

public class LoginFilter implements GlobalFilter, Ordered {

@Resource

private JwtProperties jwtProperties;

@Override

public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

//1 获得请求路径

ServerHttpRequest request = exchange.getRequest();

        String path = request.getURI().getPath();

        System.out.println(path);

//2 白名单放行

//3 获得token

String token = request.getHeaders().getFirst("Authorization");

//4 校验token

try {

            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));

        }

    }

@Override

public int getOrder() {

return 1;

    }

}

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

//处理响应异常

  $axios.onError(error => {

// token失效,服务器响应401

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

      console.error(error.response.data)

      redirect('/login')

    }

  })

  1. api.js 完整代码

var axios = null

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

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

let token = sessionStorage.getItem('token')

if( token ) {

// Adds header: `Authorization: 123` to all requests

// this.$axios.setToken('123')

    $axios.setToken( token )

  }

//处理响应异常

  $axios.onError(error => {

// token失效,服务器响应401

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

      console.error(error.response.data)

      redirect('/login')

    }

  })

//赋值

  axios = $axios

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

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

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

  inject('request', request)

}

      1. 白名单
  1. 不需要拦截的资源都配置到yml文件中,在过滤器直接放行
  2. 步骤一:修改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

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

package com.czxy.changgou4.config;

import lombok.Data;

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

import java.util.List;

/**

 * @author 桐叔

* @email liangtong@itcast.cn

 */

@Data

@ConfigurationProperties(prefix="sc.filter")

public class FilterProperties {

//允许访问路径集合

private List<String> allowPaths;

}

  1. 步骤三:修改 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;

/**

 * @author 桐叔

* @email liangtong@itcast.cn

 */

@Component

//2.1 加载JWT配置类

@EnableConfigurationProperties({FilterProperties.class} )       //加载配置类

public class LoginFilter implements GlobalFilter, Ordered {

@Resource

private FilterProperties filterProperties;

@Resource

private JwtProperties jwtProperties;

@Override

public 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 获得token

String token = request.getHeaders().getFirst("Authorization");

//4 校验token

try {

            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));

        }

    }

@Override

public int getOrder() {

return 1;

    }

}

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-10-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
验证码
腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档