学习记录5.14
关于springboot中集成邮箱验证码登录的记录
使用方式
jwt的引入:是一种认证机制,让后台知道请求是来自于受信的客户端。
user, application server和authentication server
非常常见的一个架构,首先用户需要 通过登录等手段向authentication server发送一个认证请求,authentication会返回给用户一个JWT(按照header.payload.signature的格式拼接的字符串,不保证数据泄露)
此后用户向application server发送的所有请求都要捎带上这个JWT,然后application server会验证这个JWT的合法性,验证通过则说明用户请求时来自合法守信的客户端。
在该项目中,公共模块中引入JwtHelper类:
package com.lhr.yygh.common.helper;
import io.jsonwebtoken.*;
import org.springframework.util.StringUtils;
import java.util.Date;
/**
* @author lhr
* @Date:2022/5/13 21:53
* @Version 1.0
*/
public class JwtHelper {
private static long tokenExpiration = 24*60*60*1000;
private static String tokenSignKey = "123456";
//根据参数生成token
public static String createToken(Long userId, String userName) {
String token = Jwts.builder()
.setSubject("YYGH-USER")
.setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
.claim("userId", userId)
.claim("userName", userName)
.signWith(SignatureAlgorithm.HS512, tokenSignKey)
.compressWith(CompressionCodecs.GZIP)
.compact();
return token;
}
//根据token字符串得到用户id
public static Long getUserId(String token) {
if(StringUtils.isEmpty(token)){ return null;}
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
Integer userId = (Integer)claims.get("userId");
return userId.longValue();
}
//根据token字符串得到用户名称
public static String getUserName(String token) {
if(StringUtils.isEmpty(token)) {
return "";
}
Jws<Claims> claimsJws
= Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
return (String)claims.get("userName");
}
public static void main(String[] args) {
String token = JwtHelper.createToken(1L, "55");
System.out.println(token);
System.out.println(JwtHelper.getUserId(token));
System.out.println(JwtHelper.getUserName(token));
}
}
项目逻辑:第一次登录根据手机号判断系统是否存在,如果不存在则自动注册。
实现类的方法为:
/**
* @author lhr
* @Date:2022/5/13 20:30
* @Version 1.0
*/
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo>
implements UserInfoService {
@Autowired
private RedisTemplate<String,String> redisTemplate;
//从loginVo获取输入的手机号和验证码
@Override
public Map<String, Object> login(LoginVo loginVo) {
//从loginVo获取输入的手机号,和验证码
String phone = loginVo.getPhone();
String code = loginVo.getCode();
//判断手机号和验证码是否为空
if (StringUtils.isEmpty(phone)&&StringUtils.isEmpty(code)){
throw new YyghException(ResultCodeEnum.PARAM_ERROR);
}
//手机号和验证码不为空
QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("phone", phone);
//TODO 验证码校验
String redisCode = redisTemplate.opsForValue().get(phone);
if(!code.equals(redisCode)) {
throw new YyghException(ResultCodeEnum.CODE_ERROR);
}
//获取会员
UserInfo userInfo = baseMapper.selectOne(queryWrapper);
if(null == userInfo) {
//不存在就创建
userInfo = new UserInfo();
userInfo.setName("");
userInfo.setPhone(phone);
userInfo.setStatus(1);
this.save(userInfo);
}
//校验是否被禁用
if(userInfo.getStatus() == 0) {
throw new YyghException(ResultCodeEnum.LOGIN_DISABLED_ERROR);
}
//返回页面显示名称
Map<String, Object> map = new HashMap<>();
String name = userInfo.getName();
if(StringUtils.isEmpty(name)) {
name = userInfo.getNickName();
}
if(StringUtils.isEmpty(name)) {
name = userInfo.getPhone();
}
//TODO token的生成
String token = JwtHelper.createToken(userInfo.getId(), name);
map.put("name", name);
map.put("token", token);
return map;
}
}
无注册界面,第一次登录根据手机号判断系统是否存在,如果不存在则自动注册。微信扫描登录成功必须绑定手机号码,即:第一次扫描成功后绑定手机号,以后登录扫描直接登录成功。
发送验证码:
//发送手机验证码 因为没有服务就换成邮箱验证码的发送
@GetMapping("send/{phone}")
public Result sendCode(@PathVariable String phone) {
//从redis获取验证码,如果获取获取到,返回ok
// key 手机号 code 验证码
String code = redisTemplate.opsForValue().get(phone);
if(!StringUtils.isEmpty(code)) {
return Result.ok();
}
//如果从redis获取不到,
// 生成验证码,
code = RandomUtil.getSixBitRandom();
//调用service方法,通过整合短信服务进行发送
boolean isSend = msmService.send(phone,code);
//生成验证码放到redis里面,设置有效时间
if(isSend) {
redisTemplate.opsForValue().set(phone,code,2, TimeUnit.MINUTES);
return Result.ok();
} else {
return Result.fail().message("发送短信失败");
}
}
/**
* @param phone 是要发送的邮箱账号
* @param code 随机生成的六位验证码
* @return
*/
@Override
public boolean send(String phone, String code) {
//判断邮箱是否为空
if (StringUtils.isEmpty(phone)) {
return false;
}
//调用服务直接发送邮件
// 获取发送邮箱验证码的HTML模板
TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH));
Template template = engine.getTemplate("email-code.ftl");
// 发送验证码
emailService.send(new EmailDto(Collections.singletonList(phone),
"邮箱验证码", template.render(Dict.create().set("code", code))));
return true;
}
/**
* @author lhr
* @Date:2022/5/14 17:57
* @Version 1.0
*/
@Service
public class EmailServiceImpl implements EmailService {
@Value("${spring.mail.email}")
private String email;
@Value("${spring.mail.host}")
private String host;
@Value("${spring.mail.port}")
private String port;
@Value("${spring.mail.username}")
private String username;
@Value("${spring.mail.password}")
private String password;
/**
* 发送邮件的正在环节
* @param emailDto 邮箱列表
*/
@Override
public void send(EmailDto emailDto) {
// 读取邮箱配置
if (email == null || host == null || port == null || username == null || password == null) {
throw new RuntimeException("邮箱配置异常");
}
// 设置
MailAccount account = new MailAccount();
account.setHost(host);
account.setPort(Integer.parseInt(port));
// 设置发送人邮箱
account.setFrom(username + "<" + email + ">");
// 设置发送人名称
account.setUser(username);
// 设置发送授权码
account.setPass(password);
account.setAuth(true);
// ssl方式发送
account.setSslEnable(true);
// 使用安全连接
account.setStarttlsEnable(true);
// 发送邮件
try {
int size = emailDto.getTos().size();
Mail.create(account)
.setTos(emailDto.getTos().toArray(new String[size]))
.setTitle(emailDto.getSubject())
.setContent(emailDto.getContent())
.setHtml(true)
//关闭session
.setUseGlobalSession(false)
.send();
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
}