一.引入依赖
这两个依赖分别是阿里云短信服务依赖和JWT的依赖
<dependencies>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
</dependencies>
二.使用阿里云短信做注册功能
思路:
- 使用阿里云短信服务,将用户的手机号,以及验证码存储在Redis中,并设置过期时间
- 注册前先按照手机号来获取验证码,验证码正确再进行注册操作
- 并且对参数进行校验,对密码进行加密(SpringBoot提供的DigestUtils工具)
public void register(RegisterVo registerVo) {
//获取注册的数据
String code = registerVo.getCode(); //验证码
String mobile = registerVo.getMobile(); //手机号
String nickname = registerVo.getNickname(); //昵称
String password = registerVo.getPassword(); //密码
//非空判断
if(StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)
|| StringUtils.isEmpty(code) || StringUtils.isEmpty(nickname)) {
throw new MyException("手机号不能为空");
}
//判断验证码
//获取redis验证码
String redisCode = redisTemplate.opsForValue().get(mobile);
if(!code.equals(redisCode)) {
throw new MyException("验证码错误");
}
//判断手机号是否重复,表里面存在相同手机号不进行添加
QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
wrapper.eq("mobile",mobile);
Integer count = baseMapper.selectCount(wrapper);
if(count > 0) {
throw new MyException("手机号已注册");
}
//数据添加数据库中
UcenterMember member = new UcenterMember();
member.setMobile(mobile);
member.setNickname(nickname);
member.setPassword(DigestUtils.md5DigestAsHex(password.getBytes()));//密码需要加密的
member.setIsDisabled(false);//用户不禁用
member.setAvatar("");
baseMapper.insert(member);
}
三.登录操作
当然也可以将登陆和注册写到一起,除了手机号以外其他信息采用自动生成的方式,再响应给请求一个JWT,避免用户注册之后还需要登陆的问题,这里主要的问题在于短信发送,Redis对于验证码的存储,以及JWT的使用
-
简单介绍一下JWT,和session一样是用来存储用户信息的,判断用户的登录状态
-
编写JWT工具类(这个是我随便找的一个,基本上差不多)
package com.xx.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
/**
* @author aqi
* DateTime: 2020/11/9 3:27 下午
* Description: JWT工具类
*/
public class JwtUtils {
/**
* 设置Token过期时间
*/
public static final long EXPIRE = 1000 * 60 * 60 * 24;
/**
* 秘钥
*/
public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";
/**
* 生成Token字符串(Token组成:头+荷载+签名)
* @param id 用户id
* @param nickname 用户名称
* @return Token字符串
*/
public static String getJwtToken(String id, String nickname){
return Jwts.builder()
// 设置JWT的头信息
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
// 设置Token过期时间
// 主题
.setSubject("guli-user")
// 签发时间
.setIssuedAt(new Date())
// 过期时间
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
// 设置Token主体部分
.claim("id", id)
.claim("nickname", nickname)
// 签名算法,秘钥
.signWith(SignatureAlgorithm.HS256, APP_SECRET)
.compact();
}
/**
* 判断token是否存在与有效
* @param jwtToken token
* @return 判断token是否存在与有效
*/
public static boolean checkToken(String jwtToken) {
if(StringUtils.isEmpty(jwtToken)) {
return false;
}
try {
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 判断token是否存在与有效
* @param request request
* @return 判断token是否存在与有效
*/
public static boolean checkToken(HttpServletRequest request) {
try {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) {
return false;
}
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 根据token获取会员id
* @param request request
* @return 会员id
*/
public static String getMemberIdByJwtToken(HttpServletRequest request) {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) {
return "";
}
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
Claims claims = claimsJws.getBody();
return (String)claims.get("id");
}
}
- 编写登录代码
public String login(UcenterMember member) {
//获取登录手机号和密码
String mobile = member.getMobile();
String password = member.getPassword();
//手机号和密码非空判断
if(StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) {
throw new MyException(20001,"登录失败");
}
//判断手机号是否正确
QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
wrapper.eq("mobile",mobile);
UcenterMember mobileMember = baseMapper.selectOne(wrapper);
//判断查询对象是否为空
if(mobileMember == null) {//没有这个手机号
throw new MyException(ResultCode.CODE_20001);
}
//判断密码
if(!DigestUtils.md5DigestAsHex(password.getBytes()).equals(mobileMember.getPassword())) {
throw new MyException(ResultCode.CODE_20001);
}
//判断用户是否禁用
if(mobileMember.getIsDisabled()) {
throw new MyException(ResultCode.CODE_20001);
}
//登录成功,生成token字符串
return JwtUtils.getJwtToken(mobileMember.getId(), mobileMember.getNickname());
}
- 响应给请求token字段
四.请求操作
- 请求需要在请求头中携带token信息,一般这里我们会写个AOP,或者在网关哪里写个拦截,如果没有携带token或者携带了错误的token信息,直接拦截掉
/**
* 根据token获取用户信息
*/
@GetMapping("/getMemberInfo")
public R getMemberInfo(HttpServletRequest request) {
//调用jwt工具类的方法。根据request对象获取头信息,返回用户id
String memberId = JwtUtils.getMemberIdByJwtToken(request);
if (memberId.isEmpty()) {
return R.error().data("userInfo", "登陆失效,请重新登录");
}
//查询数据库根据用户id获取用户信息
UcenterMember member = memberService.getById(memberId);
return R.success().data("userInfo",member);
}
- 响应结果
五.总结
- 使用阿里云短信服务+Redis+JWT实现这套逻辑还是比较简单的
- 阿里云使用起来很方便,只需要进行少量的配置就可以使用官方给出的模板代码进行操作
- 阿里云不提供验证码过期的服务,所以使用Redis来做,当然也可以采用其他的东西做比如SpringBoot Cache,Mongodb,或者Mysql都是可以的
- JWT使用起来也比较容易,只需要映入依赖,使用工具类就可以了
- 密码记得脱敏处理,MD5是可以加盐的,记得加上