springboot+阿里云短信+jwt+Redis+mysql实现用户手机号验证码登录
时间: 2023-10-02 21:06:00 浏览: 255
实现用户手机号验证码登录可以分为以下几个步骤:
1. 用户输入手机号和验证码,点击登录按钮。
2. 后端接收到手机号和验证码后,先验证验证码是否正确。
3. 如果验证码正确,后端生成JWT token并将token存储到Redis中,同时将token返回给前端。
4. 前端将token存储到本地,以便后续请求时使用。
5. 后续请求时,前端需要在请求头中加入token,后端通过解析token来判断用户是否已登录。
下面是具体实现过程:
1. 在阿里云短信控制台创建短信模板,获取accessKeyId和accessKeySecret。
2. 在Spring Boot项目中添加依赖:
```
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.0.3</version>
</dependency>
```
3. 实现发送短信验证码的接口:
```
@PostMapping("/sendSms")
public Result sendSms(@RequestParam("phone") String phone) {
// 生成随机验证码
String code = String.valueOf((int) ((Math.random() * 9 + 1) * 100000));
// 发送短信验证码
DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
IAcsClient client = new DefaultAcsClient(profile);
CommonRequest request = new CommonRequest();
request.setSysMethod(MethodType.POST);
request.setSysDomain("dysmsapi.aliyuncs.com");
request.setSysVersion("2017-05-25");
request.setSysAction("SendSms");
request.putQueryParameter("RegionId", "cn-hangzhou");
request.putQueryParameter("PhoneNumbers", phone);
request.putQueryParameter("SignName", "短信签名");
request.putQueryParameter("TemplateCode", "短信模板编号");
request.putQueryParameter("TemplateParam", "{\"code\":\"" + code + "\"}");
try {
CommonResponse response = client.getCommonResponse(request);
// 将验证码存储到Redis中,有效期为5分钟
redisTemplate.opsForValue().set(phone, code, 5, TimeUnit.MINUTES);
return Result.success("短信验证码发送成功");
} catch (Exception e) {
return Result.error("短信验证码发送失败");
}
}
```
4. 实现用户手机号验证码登录的接口:
```
@PostMapping("/login")
public Result login(@RequestParam("phone") String phone, @RequestParam("code") String code) {
// 验证验证码是否正确
String redisCode = redisTemplate.opsForValue().get(phone);
if (StringUtils.isBlank(redisCode)) {
return Result.error("验证码已过期,请重新发送");
}
if (!redisCode.equals(code)) {
return Result.error("验证码不正确");
}
// 生成JWT token,并存储到Redis中
String token = JwtUtils.generateToken(phone);
redisTemplate.opsForValue().set(phone, token, 1, TimeUnit.DAYS);
// 将token返回给前端
return Result.success(token);
}
```
5. 实现JWT token的生成和解析:
```
public class JwtUtils {
private static final String SECRET_KEY = "jwt_secret_key"; // JWT密钥
private static final long EXPIRATION_TIME = 7 * 24 * 60 * 60 * 1000; // JWT过期时间(7天)
public static String generateToken(String phone) {
Date now = new Date();
Date expiration = new Date(now.getTime() + EXPIRATION_TIME);
return Jwts.builder()
.setSubject(phone)
.setIssuedAt(now)
.setExpiration(expiration)
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public static String getPhoneFromToken(String token) {
try {
Claims claims = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
return claims.getSubject();
} catch (Exception e) {
return null;
}
}
}
```
6. 在拦截器中验证token并获取用户信息:
```
public class JwtInterceptor implements HandlerInterceptor {
private static final String AUTH_HEADER = "Authorization"; // token在请求头中的名称
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader(AUTH_HEADER);
if (StringUtils.isBlank(token)) {
throw new BusinessException("未登录或登录已过期");
}
String phone = JwtUtils.getPhoneFromToken(token);
if (StringUtils.isBlank(phone)) {
throw new BusinessException("无效的token");
}
String redisToken = redisTemplate.opsForValue().get(phone);
if (StringUtils.isBlank(redisToken) || !redisToken.equals(token)) {
throw new BusinessException("未登录或登录已过期");
}
return true;
}
}
```
阅读全文