123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- package com.example.xiaoshiweixinback.service;
- import cn.dev33.satoken.stp.StpUtil;
- import cn.hutool.captcha.CaptchaUtil;
- import cn.hutool.captcha.CircleCaptcha;
- import cn.hutool.core.img.ImgUtil;
- import cn.hutool.core.util.IdUtil;
- import com.alibaba.fastjson2.JSONObject;
- import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
- import com.example.xiaoshiweixinback.business.common.log.LogHelper;
- import com.example.xiaoshiweixinback.business.exception.BusinessException;
- import com.example.xiaoshiweixinback.business.exception.ExceptionEnum;
- import com.example.xiaoshiweixinback.business.jwt.JwtTokenUtil;
- import com.example.xiaoshiweixinback.business.redis.CacheTTLEnum;
- import com.example.xiaoshiweixinback.business.redis.RedisService;
- import com.example.xiaoshiweixinback.business.utils.*;
- import com.example.xiaoshiweixinback.domain.Person;
- import com.example.xiaoshiweixinback.entity.dto.person.*;
- import com.example.xiaoshiweixinback.entity.vo.PersonnelVO;
- import com.example.xiaoshiweixinback.entity.vo.person.Jscode2SessionWo;
- import com.example.xiaoshiweixinback.entity.vo.person.LoginByWxVO;
- import com.example.xiaoshiweixinback.entity.vo.person.LoginVO;
- import com.example.xiaoshiweixinback.entity.vo.person.PersonVO;
- import com.example.xiaoshiweixinback.mapper.PersonMapper;
- import com.example.xiaoshiweixinback.okhttp.ResponseManager;
- import com.example.xiaoshiweixinback.service.common.SmsService;
- import org.apache.commons.codec.binary.Base64;
- import org.apache.commons.lang3.StringUtils;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.annotation.Propagation;
- import org.springframework.transaction.annotation.Transactional;
- import javax.crypto.Cipher;
- import javax.crypto.spec.IvParameterSpec;
- import javax.crypto.spec.SecretKeySpec;
- import java.io.BufferedReader;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.io.UnsupportedEncodingException;
- import java.net.HttpURLConnection;
- import java.net.URL;
- import java.util.*;
- @Service
- public class LoginService {
- @Value("${WeChat.appId}")
- private String appId;
- @Value("${WeChat.appSecret}")
- private String appSecret;
- @Autowired
- private CacheUtil cacheUtil;
- @Autowired
- private LoginUtils loginUtils;
- @Autowired
- private JwtTokenUtil jwtTokenUtil;
- @Autowired
- private RedisService redisService;
- @Autowired
- private SmsService smsService;
- @Autowired
- private PersonMapper personMapper;
- /**
- * 手机号/账号登录
- * @param dto
- * @return
- */
- @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
- public LoginVO loginByPhone(LoginDTO dto) throws Exception {
- LogHelper.log("登录开始");
- //获取缓存中验证码
- Object codeObj = redisService.get(AppCacheKeyUtil.getLoginMessageCode(dto.getPhoneNum()));
- if (ToolUtil.isEmpty(codeObj)) {
- throw new BusinessException(ExceptionEnum.CODE_WRONG);
- }
- //校验验证码
- if (ToolUtil.isEmpty(dto.getPhoneCode())) {
- throw new BusinessException(ExceptionEnum.THE_CODE_IS_NOT_NULL);
- }
- if (!ToolUtil.equals(codeObj.toString(), dto.getPhoneCode())) {
- throw new BusinessException(ExceptionEnum.CODE_WRONG);
- }
- //校验验证码成功后使其失效
- redisService.delete(AppCacheKeyUtil.getLoginMessageCode(dto.getPhoneNum()));
- //查询用户
- LambdaQueryWrapper<Person> queryWrapper = new LambdaQueryWrapper<>();
- if (ToolUtil.isNotEmpty(dto.getPhoneNum())) {
- queryWrapper.eq(Person::getPhoneNum, dto.getPhoneNum());
- }
- Person person = personMapper.selectOne(queryWrapper);
- LoginVO loginVO = new LoginVO();
- if (ToolUtil.isEmpty(person)) {
- person = new Person();
- person.setPhoneNum(dto.getPhoneNum());
- String uid = IdUtil.simpleUUID();
- person.setUuid(uid);
- PersonnelVO personnelVO = cacheUtil.getLoginUser(loginUtils.getId());
- if (ToolUtil.isNotEmpty(personnelVO)) {
- Integer personId = personnelVO.getId();
- person.setCreateId(personId.toString());
- }
- person.setCreateTime(new Date());
- personMapper.insert(person);
- loginVO.setId(person.getId());
- loginVO.setPhone(person.getPhoneNum());
- loginVO.setIfFirst(true);
- } else {
- BeanUtil.copy(person, loginVO);
- }
- loginVO.setToken(jwtTokenUtil.createToken());
- redisService.set(AppCacheKeyUtil.getUserIdToken(loginVO.getId()),loginVO.getToken());
- redisService.set(AppCacheKeyUtil.getTokenUserInfo(loginVO.getToken()), loginVO);
- LogHelper.log("登陆结束");
- return loginVO;
- }
- /**
- * 微信小程序登录
- *
- * @param wxDTO
- * @throws Exception
- * @title: loginByWeChat
- * @author: zero
- * @date: 2024/04/01
- * @return: LoginByWxVO
- */
- public LoginByWxVO loginByWeChat(LoginByWxDTO wxDTO) throws Exception {
- String code = wxDTO.getCode();
- String encryptedData = wxDTO.getEncryptedData();
- String iv = wxDTO.getIv();
- //返回数据
- LoginByWxVO wxVO = new LoginByWxVO();
- // //1.根据code 获取微信小程序的openid和session_key
- JSONObject result = this.getSessionKeyAndOpenId(code);
- Jscode2SessionWo jscode2SessionWo = ResponseManager.parseObject(result.toString(), Jscode2SessionWo.class);
- if (ToolUtil.isNotEmpty(jscode2SessionWo)) {
- // 2. 解密用户数据
- String decryptedData = decrypt(encryptedData, jscode2SessionWo.getSession_key(), iv);
- // 3. 获取用户手机号(需要用户授权)
- String phoneNumber = "";
- JSONObject userData = JSONObject.parseObject(decryptedData);
- if (ToolUtil.isNotEmpty(userData) && userData.containsKey("purePhoneNumber")) {
- phoneNumber = userData.getString("purePhoneNumber");
- }
- // 4.查询数据表
- Person person = personMapper.selectOne(new LambdaQueryWrapper<Person>()
- .eq(Person::getOpenId, jscode2SessionWo.getOpenid()));
- if (ToolUtil.isNotEmpty(person)) {
- BeanUtil.copy(person, wxVO);
- } else {
- //添加用户表中
- person = personMapper.selectOne(new LambdaQueryWrapper<Person>()
- .eq(Person::getPhoneNum, phoneNumber));
- if (ToolUtil.isNotEmpty(person)) {
- person.setOpenId(jscode2SessionWo.getOpenid());
- person.updateById();
- } else {
- person = new Person();
- person.setPhoneNum(phoneNumber);
- person.setOpenId(jscode2SessionWo.getOpenid());
- String uid = IdUtil.simpleUUID();
- person.setUuid(uid);
- PersonnelVO personnelVO = cacheUtil.getLoginUser(loginUtils.getId());
- if (ToolUtil.isNotEmpty(personnelVO)) {
- Integer personId = personnelVO.getId();
- person.setCreateId(personId.toString());
- }
- person.setCreateTime(new Date());
- person.insert();
- wxVO.setId(person.getId());
- wxVO.setIfFirst(true);
- }
- wxVO.setPhoneNum(phoneNumber);
- }
- wxVO.setToken(jwtTokenUtil.createToken());
- wxVO.setOpenId(jscode2SessionWo.getOpenid());
- redisService.set(AppCacheKeyUtil.getUserIdToken(wxVO.getId()), wxVO);
- redisService.set(AppCacheKeyUtil.getTokenUserInfo(wxVO.getToken()), wxVO);
- }
- return wxVO;
- }
- /**
- * 发送验证码
- *
- * @param vo
- * @return
- */
- public boolean sendCode(SendCodeDTO vo) {
- boolean flag = false;
- if (!RegexUtil.isPhoneLegal(vo.getPhoneNum())) {
- throw new BusinessException(ExceptionEnum.PHONE_FORMAT_ERROR);
- }
- Object checkCode = redisService.get(AppCacheKeyUtil.getCheckCode(vo.getPhoneNum()));
- if (ToolUtil.isEmpty(checkCode)) {
- throw new BusinessException(ExceptionEnum.VERIFY_CODE);
- }
- if (ToolUtil.isNotEmpty(checkCode.toString()) && ToolUtil.equals(checkCode.toString(), vo.getCheckCode())) {
- //删除校验码
- redisService.delete(AppCacheKeyUtil.getCheckCode(vo.getPhoneNum()));
- //生成验证码
- String random = RandomUtil.getSixRandom();
- //手机号和验证码放进缓存 设置过期时间5min
- redisService.set(AppCacheKeyUtil.getLoginMessageCode(vo.getPhoneNum()), random);
- redisService.expire(AppCacheKeyUtil.getLoginMessageCode(vo.getPhoneNum()), CacheTTLEnum.FIVE_MINUTE);
- //发送短信
- smsService.sendMessage(vo.getPhoneNum(), random);
- flag = true;
- }
- return flag;
- }
- /**
- * 生成验证码
- *
- * @return 1.生成验证码的base64转码 2.生成的UUID 与Redis里面的验证码KEY值一致
- * @date: 20240401
- */
- public Map<String, String> verifyCode(PersonPhoneDTO vo) {
- if (!RegexUtil.isPhoneLegal(vo.getPhoneNum())) {
- throw new BusinessException(ExceptionEnum.PHONE_FORMAT_ERROR);
- }
- //1.定义图形验证码的长、宽、验证码字符数、干扰元素个数
- CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(200, 100, 4, 20);
- //2.生成UUID
- String uuid = cn.hutool.core.lang.UUID.fastUUID().toString();
- //3.创建返回参数 并用base64将图片转码 与UUID一起填充
- Map<String, String> result = new HashMap<>();
- result.put("captcha", ImgUtil.toBase64DataUri(captcha.getImage(), "png"));
- result.put("uuid", uuid);
- //4.将验证码存放到Redis里面
- redisService.set(AppCacheKeyUtil.getCheckCode(vo.getPhoneNum()), captcha.getCode());
- return result;
- }
- /**
- * 查询个人信息
- *
- * @param vo
- * @return
- */
- public PersonVO selectPerson(PersonIdDTO vo) {
- String token = LoginUtils.getToken();
- if (ToolUtil.isEmpty(token)) {
- throw new BusinessException(ExceptionEnum.THE_LOG_OUT);
- }
- Object obj = redisService.get(token);
- if (ToolUtil.isEmpty(obj)) {
- throw new BusinessException(ExceptionEnum.THE_LOG_OUT);
- }
- LoginVO loginVO = JSONObject.parseObject(obj.toString(), LoginVO.class);
- Person person = personMapper.selectById(loginVO.getId());
- PersonVO personVO = new PersonVO();
- BeanUtil.copy(person, personVO);
- return personVO;
- }
- /**
- * 修改个人信息
- *
- * @param vo
- * @return
- */
- @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
- public boolean editPerson(EditPersonDTO vo) {
- if (!RegexUtil.isPhoneLegal(vo.getPhoneNum())) {
- throw new BusinessException(ExceptionEnum.PHONE_FORMAT_ERROR);
- }
- long count = personMapper.selectCount(new LambdaQueryWrapper<Person>()
- .eq(Person::getPhoneNum, vo.getPhoneNum()));
- if (count > 0) {
- throw new BusinessException(ExceptionEnum.THE_PHONE_CANNOT_BE_EXIST);
- }
- Person person = personMapper.selectById(vo.getId());
- BeanUtil.copy(vo, person);
- person.updateById();
- return true;
- }
- /**
- * 退出登录
- *
- * @param dto
- * @title: sendCode
- * @author: gck
- */
- public boolean logout(PersonIdDTO dto) {
- String token = LoginUtils.getToken();
- Object obj = redisService.get(AppCacheKeyUtil.getTokenUserInfo(token));
- if (ToolUtil.isNotEmpty(obj)) {
- Object object = redisService.get(AppCacheKeyUtil.getTokenUserInfo(token));
- LoginVO loginVO = JSONObject.parseObject(object.toString(), LoginVO.class);
- redisService.delete(AppCacheKeyUtil.getUserIdToken(loginVO.getId()));
- redisService.delete(AppCacheKeyUtil.getTokenUserInfo(token));
- }
- return true;
- }
- public String getToken() {
- String uuid = UUID.randomUUID().toString().replaceAll("-", "");
- com.bjbz.common.jwt.JwtUserInfo jwtUserInfo = new com.bjbz.common.jwt.JwtUserInfo();
- jwtUserInfo.setToken(uuid);
- return jwtTokenUtil.generateToken(jwtUserInfo.toJsonString(), jwtTokenUtil.getRandomKey());
- }
- /**
- * 根据 code 获取 session_key 和 openId
- *
- * @param code 小程序登录时获取的 code
- * @return session_key 和 openId
- */
- public JSONObject getSessionKeyAndOpenId(String code) {
- String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId + "&secret=" + appSecret + "&js_code=" + code + "&grant_type=authorization_code";
- JSONObject jsonObject = null;
- try {
- // 发送请求
- URL requestUrl = new URL(url);
- HttpURLConnection connection = (HttpURLConnection) requestUrl.openConnection();
- connection.setRequestMethod("GET");
- connection.setDoOutput(true);
- connection.setDoInput(true);
- connection.connect();
- // 读取响应
- InputStream inputStream = connection.getInputStream();
- InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
- BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
- StringBuffer buffer = new StringBuffer();
- String temp = null;
- while ((temp = bufferedReader.readLine()) != null) {
- buffer.append(temp);
- }
- // 解析 JSON 数据
- jsonObject = JSONObject.parseObject(buffer.toString());
- } catch (Exception e) {
- e.printStackTrace();
- }
- return jsonObject;
- }
- /**
- * 解密用户数据
- *
- * @param encryptedData 包括敏感数据在内的完整用户信息的加密数据
- * @param sessionKey 会话密钥
- * @param iv 加密算法的初始向量
- * @return 解密后的用户数据
- */
- private static String decrypt(String encryptedData, String sessionKey, String iv) {
- byte[] encryptedDataByte = Base64.decodeBase64(encryptedData);
- byte[] sessionKeyByte = Base64.decodeBase64(sessionKey);
- byte[] ivByte = Base64.decodeBase64(iv);
- try {
- Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
- SecretKeySpec secretKeySpec = new SecretKeySpec(sessionKeyByte, "AES");
- IvParameterSpec ivParameterSpec = new IvParameterSpec(ivByte);
- cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
- byte[] decryptedDataByte = cipher.doFinal(encryptedDataByte);
- return new String(decryptedDataByte);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
- }
|