目录
校验登录
前端通过输入手机号进行验证登录,分两种状态:已注册/未注册
1.获取验证码
输入手机号,点击获取登录验证码,后端通过RandomStringUtils工具类随机生成6为数字的验证码,并通过阿里云短信模板类SmsTemplate,调用该类对象的sendSms()方法,传入手机号和验证码参数,向手机发送短信。同时将验证码存入到redis中,通过调用RedisTemplate模板类的opsForValue().set()方法,设置键值。通过设置特定字符串+手机号的方式作为我们的key,来保证数据的一致性;验证码为值进行保存,同时还设置了redis中的验证码过期时间为5分钟。
/**
* 随机生成验证码
* 发送短信验证码
* 并保存在redis中
* @param phone
*/
public void sendMsg(String phone) {
//1、随机生成6位数字
String code = RandomStringUtils.randomNumeric(6);
// String code = "123456";
//2、调用template对象,发送手机短信
template.sendSms(phone,code);
//3、将验证码存入到redis
redisTemplate.opsForValue().set("CHECK_CODE_"+phone,code, Duration.ofMinutes(5));
}
2.登录校验:分两部分,校验验证码和校验手机号。
2.1 校验验证码
首先是校验验证码是否保持一致,从redis中获取下发的验证码,通过调用RedisTemplate模板类的opsForValue().get()方法,输入特定字符串+传入的手机号作为key来获取对应的验证码值;比较redis中的验证码和用户输入的验证码码是否一致,不一致报错验证码错误或无效等字眼,反之,验证码一致则进行下一步校验手机号判断用户是否存在。
特别注意,这里验证码使用一次后要记得删除验证码,保证验证码一次性。通过调用RedisTemplate模板类delete()方法输入特定的key进行删除操作。
2.2 校验手机号
通过使用Mybatis-plus里面的通用mapper来实现对数据库单表的CRUD操作。在Mapper的接口上继承BaseMapper的接口,其泛型为需要操作的数据库表对应的实体类。
根据手机号查询对应的用户信息并返回,使用QueryWrapper封装查询条件,使用通用mapper的selectOne()方法传入条件对象并返回一个用户对象。对返回的用户进行判断,如果为null就是未注册,不为null说明已经注册过。
定义一个布尔变量会返回给前端,false就是老用户,true就表示新用户。前端用来判断是新老用户,从而进行不同的页面跳转。
2.3 用户注册保存
如果用户为null,创建一个新用户并保存相关的信息,在进行设置保存密码时,通过DigestUtils.md5Hex("123456")进行加密;这里默认设置密码为123456。使用通用mapper的insert()方法保存用户,获取用户的id并返回,然后将id赋值给刚刚新创建的用户对象;这时设置布尔变量为true。
2.4 JWT生成token,颁发令牌
创建一个Map集合存入id和手机号码等用户的信息,并通过JwtUtils工具类的getToken()方法,传入map对用户信息构建生成token字符串;
通过实现JwtBuilder接口,调用Jwts.builder()返回一个新的JwtBuilder实例,它可以配置,然后用于创建JWT紧凑序列化字符串。
通过signWith()选择设置算法和加密方式,使用指定的算法和指定的密钥为构造JWT签名,生成JWS。
通过setExpiration()设置过期时间,addClaims()添加将所有给定的名称/值对添加到有效负载中的JSON Claims中。
通过compact()构建JWT并根据JWT compact Serialization规则将其序列化为紧凑的url安全字符串并返回。
最后将用户信息构建加密,返回一个token字符串,并对其进行保存返回。
特别注意:生成的token会保存在前端请求头中,保存在Authorization中,获取请求头中Authorization对应的值,就是在获取token用于身份校验。
JwtUtils工具类
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang3.StringUtils;
import java.util.Date;
import java.util.Map;
public class JwtUtils {
// TOKEN的有效期1小时(S)
private static final int TOKEN_TIME_OUT = 3_600;
// 加密KEY
private static final String TOKEN_SECRET = "itcast";
// 生成Token
public static String getToken(Map params){
long currentTime = System.currentTimeMillis();
return Jwts.builder()
.signWith(SignatureAlgorithm.HS512, TOKEN_SECRET) //加密方式
.setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000)) //过期时间戳
.addClaims(params)
.compact();
}
/**
* 获取Token中的claims信息
*/
public static Claims getClaims(String token) {
return Jwts.parser()
.setSigningKey(TOKEN_SECRET)
.parseClaimsJws(token).getBody();
}
/**
* 是否有效 true-有效,false-失效
*/
public static boolean verifyToken(String token) {
if(StringUtils.isEmpty(token)) {
return false;
}
try {
Claims claims = Jwts.parser()
.setSigningKey("itcast")
.parseClaimsJws(token)
.getBody();
}catch (Exception e) {
return false;
}
return true;
}
}
2.5 构造返回值
创建一个Map集合封装键值,根据前端要求添加token和布尔变量两个键值对,通过ResponseEntity封装实体并响应给前端。token用于身份校验,布尔变量用于判断新老用户。
如果是老用户,校验成功直接跳转到主页;如果是新用户,校验成功跳转页面进行信息完善页面。
校验登录方法— UserService
/**
* 验证登录
* @param phone
* @param code
*/
public Map loginVerification(String phone, String code) {
//1、从redis中获取下发的验证码
String redisCode = redisTemplate.opsForValue().get("CHECK_CODE_" + phone);
//2、对验证码进行校验(验证码是否存在,是否和输入的验证码一致)
if(StringUtils.isEmpty(redisCode) || !redisCode.equals(code)) {
//验证码无效
throw new RuntimeException("验证码错误");
}
//3、删除redis中的验证码
redisTemplate.delete("CHECK_CODE_" + phone);
//4、通过手机号码查询用户
User user = userApi.findByMobile(phone);
boolean isNew = false;
//5、如果用户不存在,创建用户保存到数据库中
if(user == null) {
user = new User();
user.setMobile(phone);
user.setPassword(DigestUtils.md5Hex("123456"));
Long userId = userApi.save(user);
user.setId(userId);
isNew = true;
}
//6、通过JWT生成token(存入id和手机号码)
Map tokenMap = new HashMap();
tokenMap.put("id",user.getId());
tokenMap.put("mobile",phone);
String token = JwtUtils.getToken(tokenMap);
//7、构造返回值
Map retMap = new HashMap();
retMap.put("token",token);
retMap.put("isNew",isNew);
return retMap;
}
获取登录验证码及校验登录—— UserController
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
/**
* 获取登录验证码
* 请求参数:phone (Map)
* 响应:void
* ResponseEntity
*/
@PostMapping("/login")
public ResponseEntity login(@RequestBody Map map){
String phone =(String) map.get("phone");
userService.sendMsg(phone);
//return ResponseEntity.status(500).body("出错啦");
return ResponseEntity.ok(null); //正常返回状态码200
}
/**
* 检验登录
*/
@PostMapping("/loginVerification")
public ResponseEntity loginVerification(@RequestBody Map map) {
//1、调用map集合获取请求参数
String phone = (String) map.get("phone");
String code = (String) map.get("verificationCode");
//2、调用userService完成用户登录
Map retMap = userService.loginVerification(phone,code);
//3、构造返回
return ResponseEntity.ok(retMap);
}
}