基本思路
获取验证码接口
验证码操作用了com.pig4cloud.plugin的captcha-core这个库。
- AccountControl的"/checkCode"接口代码,通过ArithmeticCaptcha生成一张验证码图片,通过text()函数得到验证码的答案保存到变量code,然后把图片转为base64方便传输保存到变量checkCodeBase64 。
- 把code作为参数传入函数saveCheckCode,把函数返回结果保存到变量checkCodeKey 。
- 创建一个Map,checkCodeBase64和checkCodeKey放进去,然后丢给getSuccessResponseVO()当作响应消息返回。
@RequestMapping("/checkCode")
public ResponseVO checkCode(){
//生成验证码
ArithmeticCaptcha captcha = new ArithmeticCaptcha(100,42);
//获取验证码
String code = captcha.text();
//生成base64
String checkCodeBase64 = captcha.toBase64();
//把验证码存入redis
String checkCodeKey = redisComponet.saveCheckCode(code);
Map<String,String> result = new HashMap<>();
result.put("checkCode",checkCodeBase64);
result.put("checkCodeKey",checkCodeKey);
return getSuccessResponseVO(result);
}
- saveCheckCode函数负责把验证码答案code保存到redis,生成一个随机的 UUID作为存到redis的key,把这个key返回出去。
public String saveCheckCode(String code){
String checkCodeKey = UUID.randomUUID().toString();
redisUtils.setex(Constants.REDIS_KEY_CHECK_CODE+checkCodeKey,code,Constants.REDIS_KEY_EXPIRE_ONE_MIN);
return checkCodeKey;
}
接口执行结果:
用户注册
- 根据传入的验证码答案checkCode判断是否与redis储存的值相等。
- 验证码通过后执行register服务函数。
- 执行完try的代码,执行finally代码删除redis相应的键值。
@RequestMapping("/register")
public ResponseVO register(@NotEmpty @Email @Size(max = 150) String email,
@NotEmpty @Size(max = 20) String nickName,
@NotEmpty @Pattern(regexp = Constants.REGEX_PASSWORD) String registerPassword,
@NotEmpty String checkCodeKey,
@NotEmpty String checkCode){
try {
if(!checkCode.equalsIgnoreCase(redisComponet.getCheckeCode(checkCodeKey))){
throw new BusinessException("图片验证码不正确");
}
userInfoService.register(email,nickName,registerPassword);
return getSuccessResponseVO(null);
} catch (BusinessException e) {
throw new RuntimeException(e);
} finally {
redisComponet.cleanCheckCode(checkCodeKey);
}
}
- 检查邮箱和昵称是否已存在。
- mybatis操作数据库创建新注册的用户信息。
@Override
public void register(String email, String nickName, String registerPassword) throws BusinessException {
UserInfo userInfo = this.userInfoMapper.selectByEmail(email);
if(null!=userInfo){
throw new BusinessException("邮箱已被注册");
}
UserInfo nickNameUser = this.userInfoMapper.selectByNickName(nickName);
if(null!=nickNameUser){
throw new BusinessException("昵称已被注册");
}
userInfo = new UserInfo();
String userId = StringTools.getRandomNumber(Constants.USERID_LENGTH);
userInfo.setUserId(userId);
userInfo.setEmail(email);
userInfo.setNickName(nickName);
userInfo.setPassword(StringTools.encodeByMd5(registerPassword));
userInfo.setJoinTime(new Date());
userInfo.setStatus(UserStatusEnum.NORMAL.getCode());
userInfo.setTotalCoin(0);
this.userInfoMapper.insert(userInfo);
}
执行结果:
输入对应的参数
注册成功
检查数据库
登录接口
- 传入email、password、checkCodeKey和checkCode,校验验证码。
- 获取登录的ip地址,向登录服务函数login传入email、password和ip返回dto对象,把该dto对象保存。
- SaveTokenToCookie函数作用是保存dto里的token到浏览器中,方便前端获取。
- 执行完try的代码,执行finally代码删除redis相应的键值。
@RequestMapping("/login")
public ResponseVO login(HttpServletResponse response,
@NotEmpty @Email String email,
@NotEmpty String password,
@NotEmpty String checkCodeKey,
@NotEmpty String checkCode){
try {
if(!checkCode.equalsIgnoreCase(redisComponet.getCheckeCode(checkCodeKey))){
throw new BusinessException("图片验证码不正确");
}
String ip = getIpAddr();
TokenUserInfoDTO tokenUserInfoDTO = userInfoService.login(email ,password,ip);
SaveTokenToCookie(response,tokenUserInfoDTO.getToken());
return getSuccessResponseVO(tokenUserInfoDTO);
} catch (BusinessException e) {
throw new RuntimeException(e);
} finally {
redisComponet.cleanCheckCode(checkCodeKey);
}
}
获取用户IP地址的函数
protected String getIpAddr() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String ip = request.getHeader("x-forwarded-for");
if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
// 多次反向代理后会有多个ip值,第一个ip才是真实ip
if (ip.indexOf(",") != -1) {
ip = ip.split(",")[0];
}
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
在浏览器保存token
protected void SaveTokenToCookie(HttpServletResponse response,String token) {
Cookie cookie = new Cookie(Constants.TOKEN_WEB,token);
cookie.setMaxAge(Constants.REDIS_KEY_EXPIRE_ONE_DAY / 1000 * 7);
cookie.setPath("/");
response.addCookie(cookie);
}
- 检验邮箱、密码和用户状态,抛出对应的异常。
- 登录信息无误,根据用户ID更新ip和最新登录时间。
- 对象拷贝,将一个对象userInfo的属性复制到DTO对象中(不会复制DTO中没有的属性)。
- 把DTO对象保存到redis中,token的内容是保存到redis的key值。
@Override
public TokenUserInfoDTO login(String email, String password, String ip) throws BusinessException {
UserInfo userInfo = this.userInfoMapper.selectByEmail(email);
if(null==userInfo || !userInfo.getPassword().equals(StringTools.encodeByMd5(password))){
throw new BusinessException("用户名或密码错误");
}
if(UserStatusEnum.FORBIDDEN.getCode().equals(userInfo.getStatus())){
throw new BusinessException("用户已被禁用");
}
UserInfo updateInfo = new UserInfo();
updateInfo.setLastLogin(new Date());
updateInfo.setLastIp(ip);
this.userInfoMapper.updateByUserId(updateInfo,userInfo.getUserId());
TokenUserInfoDTO tokenUserInfoDTO = CopyTools.copy(userInfo, TokenUserInfoDTO.class);
redisComponet.saveTokenInfo(tokenUserInfoDTO);
return tokenUserInfoDTO;
}
执行结果: