loading...
登录注册
Published in:2021-04-25 |
Words: 3.6k | Reading time: 17min | reading:

登录注册模块

  • 1.获取用户的手机号,发送短信,进行短信验证登录
  • 2.登录成功后判断是否为新用户
  • 3.如果是新用户需要完善个人资料,上传头像信息
  • 4.如果是老用户,进入首页

获取验证码

  • 1.获取用户的手机号
  • 2.拿着用户的用户的手机号拼接好的redisKey去redis中查找验证码是否过期,如果没有过期直接返回
  • 3.如果验证码为空的话,调用阿里云的Sms短信平台进行短信验证码的获取
  • 4.这里需要进行判断阿里云的Sms是否成功发送短信,判断获取到的验证码是否为空,为空记录日志报异常,不为空的话,将其存入到redis中,并设置其过期时间即可。保存的时候需要保存包含手机号的redisKey,验证码和过期时间
controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* 发送短信验证码接口
*
* @param param
* @return
*/
@PostMapping("/login")
public ResponseEntity<ErrorResult> sendCheckCode(@RequestBody Map<String, String> param) {

//获取到请求的手机号
String phone = param.get("phone");
ErrorResult errorResult = null;

try {
//调用service层的发送短信验证码的方法,errorResult表示返回给前台的对象,未发生错误返回200,发生错误返回该对象
errorResult = smsService.sendCheckCode(phone);
//如果errorResult为空的话,证明发送成功,返回200
if (null == errorResult) {
return ResponseEntity.ok(null);
}
} catch (Exception e) {
//try发生异常,捕获异常信息,并打印手机号和异常
log.error("发送验证码失败~ phone = " + phone, e);
//发生异常构建errorResult对象
errorResult = ErrorResult.builder().errCode("000002").errMessage("短信验证码发送失败~").build();
}
//发生错误返回catch中构建的异常对象
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResult);
}
service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/**
* 阿里云接口获取短信验证码
*
* @param mobile
* @return
*/
public String sendSms(String mobile) {
DefaultProfile profile = DefaultProfile.getProfile(this.aliyunSMSConfig.getRegionId(),
this.aliyunSMSConfig.getAccessKeyId(), this.aliyunSMSConfig.getAccessKeySecret());
IAcsClient client = new DefaultAcsClient(profile);

String code = RandomUtils.nextInt(100000, 999999) + "";

CommonRequest request = new CommonRequest();
request.setSysMethod(MethodType.POST);
request.setSysDomain(this.aliyunSMSConfig.getDomain());
request.setSysVersion("2017-05-25");
request.setSysAction("SendSms");
request.putQueryParameter("RegionId", this.aliyunSMSConfig.getRegionId());
request.putQueryParameter("PhoneNumbers", mobile); //目标手机号
request.putQueryParameter("SignName", this.aliyunSMSConfig.getSignName()); //签名名称
request.putQueryParameter("TemplateCode",
this.aliyunSMSConfig.getTemplateCode()); //短信模板code
request.putQueryParameter("TemplateParam", "{\"code\":\"" + code + "\"}");//模板中变量替换
try {
CommonResponse response = client.getCommonResponse(request);
String data = response.getData();
if (StringUtils.contains(data, "\"Message\":\"OK\"")) {
return code;
}
log.info("发送短信验证码失败~ data = " + data);
} catch (Exception e) {
log.error("发送短信验证码失败~ mobile = " + mobile, e);
}
return null;
}


/**
* 发送短信验证码
* 实现:发送完成短信验证码后,需要将验证码保存到redis中
*
* @param phone
* @return
*/
public ErrorResult sendCheckCode(String phone) {

//设置redis中的key
String redisKey = "CHECK_CODE_" + phone;

//先判断该手机号发送的验证码是否还未失效
if (this.redisTemplate.hasKey(redisKey)) {
String msg = "上一次发送的验证码还未失效!";
return ErrorResult.builder().errCode("000001").errMessage(msg).build();
}

//获取验证码
// String code = this.sendSms(phone);
String code = "123456";

//如果验证码为空的话
if (StringUtils.isEmpty(code)) {
String msg = "发送短信验证码失败!";
return ErrorResult.builder().errCode("000000").errMessage(msg).build();
}

//短信发送成功,将验证码保存到redis中,有效期为5分钟
this.redisTemplate.opsForValue().set(redisKey, code, Duration.ofMinutes(5));
return null;
}

进行用户的登录

  • 1.用户获取到验证码之后,需要进行验证码的登录验证,此时需要获取到用户的手机号和验证码
  • 2.在获取到用户的手机号和验证码之后,需要在redis中取查找是否存在验证码,进行验证码的判断
  • 3.根据用户的手机号去redis中查找该手机号对应的验证码进行判断,如果用户输入的验证码与redis中存放的验证码不一致,验证码错误
  • 4.如果验证码判断通过,需要在redis中清除此验证码。接下来判断此用户是否是新用户
  • 5.通过用户的手机号去数据库中查找是否存在用户,如果为空则为新用户,新用户需要进行资料的完善;不为空则为老用户
  • 6.如果为新用户的话,设置其信息保存至数据库中,并标记为新用户,进行后续资料完善的判断依据
  • 7.在通过验证码的校验,Jwt会生成一个此次登录的token,进行单点登录的认证
  • 8.登录成功后,通过rocketMQ发送消息,将用户登录成功的消息发送给其他系统
controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* 用户登录
*
* @param param
* @return
*/
@RequestMapping("/loginVerification")
public ResponseEntity<Object> login(@RequestBody Map<String, String> param) {

try {
//获取手机号和验证码
String phone = param.get("phone");
String code = param.get("verificationCode");

//调用userService的login方法进行判断
String data = userService.login(phone, code);

if (StringUtils.isNoneEmpty(data)) {
//登录成功
//存放用于返回的结果对象
HashMap<String, Object> result = new HashMap<>(2);
//切割data,获取属性值
String[] split = StringUtils.split(data, '|');

result.put("token", split[0]);
result.put("isNew", Boolean.valueOf(split[1]));

return ResponseEntity.ok(result);
}
} catch (Exception e) {
e.printStackTrace();
}
//发生异常构建errorResult对象
ErrorResult errorResult = ErrorResult.builder().errCode("000002").errMessage("登录失败!")
.build();

//发生错误返回catch中构建的异常对象
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResult);
}
Service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/**
* 用户登录
*
* @param phone 手机号
* @param code 验证码
* @return
*/
public String login(String phone, String code) {

//设置redis中的key
String redisKey = "CHECK_CODE_" + phone;

//默认为老用户
boolean isNew = false;

//获取redis中存放的验证码
String redisData = redisTemplate.opsForValue().get(redisKey);

//如果redis存放的验证码和用户输入的验证码不一致,返回null
if (!StringUtils.equals(code, redisData)) {
//验证码错误,直接返回null
return null;
}

//在校验成功验证码之后,需要废弃此验证码
redisTemplate.delete(redisKey);

//根据phone查询user,判断是否数据库中是否存在此用户
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("mobile", phone);
User user = userMapper.selectOne(queryWrapper);

//如果user为null,则为新用户,进行注册,保存到数据库中
if (null == user) {
user = new User();
user.setMobile(phone);
//使用md5对密码进行加密
user.setPassword(DigestUtils.md5Hex("123456"));

userMapper.insert(user);
//新用户
isNew = true;
}

Map<String, Object> claims = new HashMap<String, Object>();
claims.put("id", user.getId());
//生成token
String token = Jwts.builder()
.setClaims(claims) //payload,存放数据的位置,不能放置敏感数据,如:密码等
.signWith(SignatureAlgorithm.HS256, secret) //设置加密方法和加密盐
.setExpiration(new DateTime().plusHours(12).toDate()) //设置过期时间,12小时后过期
.compact();

try {
//发送用户登录成功的消息给其他系统
HashMap<String, Object> map = new HashMap<>();
map.put("id", user.getId());
map.put("date", System.currentTimeMillis());

rocketMQTemplate.convertAndSend("tanhua-sso-login", map);
} catch (MessagingException e) {
log.error("发送消息失败!", e);
}

return token + "|" + isNew;
}

完善新用户的个人信息

  • 1.首先获取到用户的信息和该次请求携带的token,进行token的校验
  • 2.token的校验,需要通过token解析器进行解析,成功解析返回一个map集合,获取map集合的key封装到用户对象中,通过获取到该用户的id在redis中查找是否存在此手机号,存在的话直接返回该用户对象,不存在的话去数据库中查询,查询到的话获取其手机号,并将其写入到redis中,并且需要设置其过期时间,过期时间需要和token保持一致,如果数据库中也不存在的话也就是token解析失败,token不合法或者已经过期,返回null.
  • 3.如果用户对象不为空的话,token校验通过,设置该用户的个人资料进行数据库的保存操作
controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 完善个人资料-基本信息
*
* @param param
* @return
*/
@RequestMapping("/loginReginfo")
public ResponseEntity<Object> loginReginfo(@RequestBody Map<String, String> param,
@RequestHeader("Authorization") String token) {

try {
boolean bool = userInfoService.saveUserInfo(param, token);
return ResponseEntity.ok(null);

} catch (Exception e) {
e.printStackTrace();
}

//发生异常构建errorResult对象
ErrorResult errorResult = ErrorResult.builder().errCode("000001").errMessage("保存用户信息失败!")
.build();
//发生错误返回catch中构建的异常对象
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResult);
}
service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* 保存用户的基本信息
*
* @param param
* @param token
* @return
*/
public boolean saveUserInfo(Map<String, String> param, String token) {

//校验token
User user = userService.queryUserByToken(token);

//校验token未通过,返回false
if (null == user) {
return false;
}

//校验通过
UserInfo userInfo = new UserInfo();
userInfo.setUserId(user.getId());
userInfo.setSex(StringUtils.equalsIgnoreCase(param.get("gender"), "man") ? GenderEnum.MAN
: GenderEnum.WOMAN);
userInfo.setNickName(param.get("nickname"));
userInfo.setBirthday(param.get("birthday"));
userInfo.setCity(param.get("city"));
return userInfoMapper.insert(userInfo) == 1;
}
校验token
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* 校验token
*
* @param token
* @return
*/
public User queryUserByToken(String token) {
try {
// 通过token解析数据
Map<String, Object> body = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
User user = new User();
user.setId(Long.valueOf(body.get("id").toString()));

//需要返回user对象中的mobile,需要查询数据库获取到mobile数据
//如果每次都查询数据库,必然会导致性能问题,需要对用户的手机号进行缓存操作
//数据缓存时,需要设置过期时间,过期时间要与token的时间一致
//如果用户修改了手机号码,需要同步修改redis中的数据
String redisKey = "TANHUA_USER_MOBILE_" + user.getId();
if (redisTemplate.hasKey(redisKey)) {
String mobile = redisTemplate.opsForValue().get(redisKey);
user.setMobile(mobile);
} else {
//查询数据库
User u = userMapper.selectById(user.getId());
user.setMobile(u.getMobile());

//将手机号写入到redis中
//在jwt中的过期时间的单位为:秒
long timeout = Long.valueOf(body.get("exp").toString()) * 1000 - System
.currentTimeMillis();
redisTemplate.opsForValue()
.set(redisKey, u.getMobile(), timeout, TimeUnit.MILLISECONDS);
}
return user;
} catch (ExpiredJwtException e) {
log.info("token已经过期!token = " + token);
} catch (Exception e) {
log.error("token不合法! token = " + token);
}
return null;
}

图片上传(用户的头像)

  • 1.获取用户的头像MultipartFile file 和 token
  • 2.进行token的校验
  • 3.校验token通过后,需要检验图片是否为人像,如果不是人像则返回false
  • 4.检验图片是否为人像,需要调用虹软开发平台的图片人像检验功能,第三方工具
  • 5.如果是人像的话,将头像上传到阿里云oss进行图片的存储
  • 6.上传成功后需要将头像的链接更新到数据库中的用户表中进行存储
controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 完善个人资料-用户头像
*
* @param param
* @return
*/
@RequestMapping("/loginReginfo/head")
public ResponseEntity<Object> loginRegLogo(@RequestParam("headPhoto") MultipartFile file,
@RequestHeader("Authorization") String token) {

try {
boolean bool = userInfoService.saveUserLogo(file, token);
return ResponseEntity.ok(null);
} catch (Exception e) {
e.printStackTrace();
}

//发生异常构建errorResult对象
ErrorResult errorResult = ErrorResult.builder().errCode("000001").errMessage("保存用户头像失败!")
.build();

//发生错误返回catch中构建的异常对象
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResult);
}
service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* 用户的头像上传
*
* @param file
* @param token
* @return
*/
public boolean saveUserLogo(MultipartFile file, String token) {

//校验token
User user = userService.queryUserByToken(token);

//校验token未通过,返回false
if (null == user) {
return false;
}

try {
//检验图片是否是人像,如果不是人像就返回false
boolean b = faceEngineService.checkIsPortrait(file.getBytes());
if (!b) {
return false;
}
} catch (IOException e) {
e.printStackTrace();
}

//如果是人像,将头像上传到阿里云oss
PicUploadResult result = picUploadService.upload(file);
if (StringUtils.isEmpty(result.getName())) {
return false;
}

//把头像保存到用户信息表中
UserInfo userInfo = new UserInfo();
userInfo.setLogo(result.getName());
QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_id", user.getId());

return userInfoMapper.update(userInfo, queryWrapper) == 1;
}
检验头像是否为人像
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
package com.yishan.service;

import com.arcsoft.face.EngineConfiguration;
import com.arcsoft.face.FaceEngine;
import com.arcsoft.face.FaceInfo;
import com.arcsoft.face.FunctionConfiguration;
import com.arcsoft.face.enums.DetectMode;
import com.arcsoft.face.enums.DetectOrient;
import com.arcsoft.face.enums.ErrorInfo;
import com.arcsoft.face.enums.ImageFormat;
import com.arcsoft.face.toolkit.ImageFactory;
import com.arcsoft.face.toolkit.ImageInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

@Service
public class FaceEngineService {

private static final Logger LOGGER = LoggerFactory.getLogger(FaceEngineService.class);

@Value("${arcsoft.appid}")
private String appid;

@Value("${arcsoft.sdkKey}")
private String sdkKey;

@Value("${arcsoft.libPath}")
private String libPath;

private FaceEngine faceEngine;

@PostConstruct
public void init() {
// 激活并且初始化引擎
FaceEngine faceEngine = new FaceEngine(libPath);
int activeCode = faceEngine.activeOnline(appid, sdkKey);
if (activeCode != ErrorInfo.MOK.getValue() && activeCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) {
LOGGER.error("引擎激活失败");
throw new RuntimeException("引擎激活失败");
}

//引擎配置
EngineConfiguration engineConfiguration = new EngineConfiguration();
//IMAGE检测模式,用于处理单张的图像数据
engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
//人脸检测角度,全角度
engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);

//功能配置
FunctionConfiguration functionConfiguration = new FunctionConfiguration();
functionConfiguration.setSupportAge(true);
functionConfiguration.setSupportFace3dAngle(true);
functionConfiguration.setSupportFaceDetect(true);
functionConfiguration.setSupportFaceRecognition(true);
functionConfiguration.setSupportGender(true);
functionConfiguration.setSupportLiveness(true);
functionConfiguration.setSupportIRLiveness(true);
engineConfiguration.setFunctionConfiguration(functionConfiguration);

//初始化引擎
int initCode = faceEngine.init(engineConfiguration);

if (initCode != ErrorInfo.MOK.getValue()) {
LOGGER.error("初始化引擎出错!");
throw new RuntimeException("初始化引擎出错!");
}

this.faceEngine = faceEngine;
}

/**
* 检测图片是否为人像
*
* @param imageInfo 图像对象
* @return true:人像,false:非人像
*/
public boolean checkIsPortrait(ImageInfo imageInfo) {
// 定义人脸列表
List<FaceInfo> faceInfoList = new ArrayList<FaceInfo>();
faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), ImageFormat.CP_PAF_BGR24, faceInfoList);
return !faceInfoList.isEmpty();
}

public boolean checkIsPortrait(byte[] imageData) {
return this.checkIsPortrait(ImageFactory.getRGBData(imageData));
}

public boolean checkIsPortrait(File file) {
return this.checkIsPortrait(ImageFactory.getRGBData(file));
}

}
上传图片到阿里云oss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package com.yishan.service;


import com.aliyun.oss.OSSClient;
import com.yishan.config.AliyunConfig;
import com.yishan.vo.PicUploadResult;
import java.io.ByteArrayInputStream;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

@Service
public class PicUploadService {

// 允许上传的格式
private static final String[] IMAGE_TYPE = new String[]{".bmp", ".jpg",
".jpeg", ".gif", ".png"};

@Autowired
private OSSClient ossClient;

@Autowired
private AliyunConfig aliyunConfig;

public PicUploadResult upload(MultipartFile uploadFile) {

PicUploadResult fileUploadResult = new PicUploadResult();

//图片做校验,对后缀名
boolean isLegal = false;

//循环校验文件的后缀名,符合的返回true 执行标记
for (String type : IMAGE_TYPE) {
if (StringUtils.endsWithIgnoreCase(uploadFile.getOriginalFilename(),
type)) {
isLegal = true;
break;
}
}

//不符合后缀名的返回false,不进行上传
if (!isLegal) {
fileUploadResult.setStatus("error");
return fileUploadResult;
}

// 文件新路径
String fileName = uploadFile.getOriginalFilename();
String filePath = getFilePath(fileName);

// 上传到阿里云
try {
// 目录结构:images/2018/12/29/xxxx.jpg
ossClient.putObject(aliyunConfig.getBucketName(), filePath, new
ByteArrayInputStream(uploadFile.getBytes()));
} catch (Exception e) {
e.printStackTrace();
//上传失败
fileUploadResult.setStatus("error");
return fileUploadResult;
}

// 上传成功
fileUploadResult.setStatus("done");
fileUploadResult.setName(this.aliyunConfig.getUrlPrefix() + filePath);
fileUploadResult.setUid(String.valueOf(System.currentTimeMillis()));

return fileUploadResult;
}

private String getFilePath(String sourceFileName) {
DateTime dateTime = new DateTime();
return "images/" + dateTime.toString("yyyy")
+ "/" + dateTime.toString("MM") + "/"
+ dateTime.toString("dd") + "/" + System.currentTimeMillis() +
RandomUtils.nextInt(100, 9999) + "." +
StringUtils.substringAfterLast(sourceFileName, ".");
}
}
Prev:
BIO、NIO、AIO
catalog
catalog