添加了微信小程序授权登录的功能模块(单独运行)

This commit is contained in:
DESKTOP-G8BCEP0\HP 2021-09-09 12:01:03 +08:00
parent f0d0f473ef
commit 12edd5dc0b
11 changed files with 757 additions and 0 deletions

View File

@ -149,6 +149,11 @@
<artifactId>poi-ooxml</artifactId> <artifactId>poi-ooxml</artifactId>
<version>${poi-ooxml.version}</version> <version>${poi-ooxml.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.7.0</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -0,0 +1,94 @@
package com.xkrs.common.tool;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
/**
* @Author Scott
* @Date 2018-07-12 14:23
* @Desc JWT工具类
**/
public class JwtUtil {
/**
* Token过期时间30分钟用户登录过期时间是此时间的两倍以token在reids缓存时间为准
*/
public static final long EXPIRE_TIME = 30 * 60 * 1000;
/**
* 校验token是否正确
*
* @param token 密钥
* @param secret 用户的密码
* @return 是否正确
*/
public static boolean verify(String token, String username, String secret) {
try {
// 根据密码生成JWT效验器
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm).withClaim("username", username).build();
// 效验TOKEN
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (Exception exception) {
return false;
}
}
/**
* 获得token中的信息无需secret解密也能获得
*
* @return token中包含的用户名
*/
public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username").asString();
} catch (JWTDecodeException e) {
return null;
}
}
/**
* 生成签名,5min后过期
*
* @param username 用户名
* @param secret 用户的密码
* @return 加密的token
*/
public static String sign(String username, String secret) {
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(secret);
// 附带username信息
return JWT.create().withClaim("username", username).withExpiresAt(date).sign(algorithm);
}
/**
* 根据request中的token获取用户账号
*
* @param request
* @return
* @throws
*/
public static String getUserNameByToken(HttpServletRequest request){
String accessToken = request.getHeader("Authorization");
if(accessToken ==null){
return null;
}
String username = getUsername(accessToken);
return username;
}
public static void main(String[] args) {
String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjUzMzY1MTMsInVzZXJuYW1lIjoiYWRtaW4ifQ.xjhud_tWCNYBOg_aRlMgOdlZoWFFKB_givNElHNw3X0";
System.out.println(JwtUtil.getUsername(token));
}
}

View File

@ -0,0 +1,46 @@
package com.xkrs.controller;
import com.xkrs.service.AppletsUserService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.Map;
/**
* @author HP
*/
@RestController
public class AppletsUserController {
@Resource
private AppletsUserService appletsUserService;
/**
* 微信小程序登录
* @param map
* @return
*/
@PostMapping("/userLogin")
public String userLogin(@RequestBody Map map){
String avatarUrl = (String) map.get("avatarUrl");
String nickName = (String) map.get("nickName");
Integer sex = (Integer) map.get("sex");
String code = (String) map.get("code");
return appletsUserService.userLogin(avatarUrl,nickName,sex,code);
}
/**
* 解析手机号
* @param map
* @return
*/
@PostMapping("/decodePhone")
public String decodePhone(@RequestBody Map map){
String encryptedData = (String) map.get("encryptedData");
String iv = (String) map.get("iv");
String openId = (String) map.get("openId");
return appletsUserService.decodePhone(encryptedData,iv,openId);
}
}

View File

@ -0,0 +1,39 @@
package com.xkrs.dao;
import com.xkrs.model.entity.AppletsUser;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Component;
/**
* @author XinYi Song
*/
@Component
public interface AppletsUserDao extends JpaRepository<AppletsUser,Long> {
/**
* 根据openid查询用户信息
* @param openId
* @return
*/
AppletsUser findByOpenId(String openId);
/**
* 根据用户id修改SessionKey
* @param userId
* @param SessionKey
*/
@Query(value = "update applets_user set session_key = ?2 where id = ?1",nativeQuery = true)
@Modifying(clearAutomatically=true)
void updateSessionKey(Integer userId,String SessionKey);
/**
* 根据openId修改手机号
* @param openId
* @param phone
*/
@Query(value = "update applets_user set user_phone = ?2 where open_id = ?1",nativeQuery = true)
@Modifying(clearAutomatically=true)
void updatePhone(String openId,String phone);
}

View File

@ -127,4 +127,11 @@ public interface SysUserDao extends JpaRepository<SysUserEntity,Integer> {
*/ */
@Query(value = "select * from sys_user where id = ?",nativeQuery = true) @Query(value = "select * from sys_user where id = ?",nativeQuery = true)
SysUserEntity selectByUserId(Integer userId); SysUserEntity selectByUserId(Integer userId);
/**
* 根据openId查询用户的信息
* @param openId
* @return
*/
SysUserEntity findByOpenId(String openId);
} }

View File

@ -0,0 +1,153 @@
package com.xkrs.model.entity;
import javax.persistence.*;
import java.time.LocalDateTime;
/**
* 小程序用户表
* @author XinYi Song
*/
@Entity
@Table(name="applets_user")
public class AppletsUser {
/**
* 指定主键建立自增序列主键值取自序列
*/
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "applets_user_seq_gen")
@SequenceGenerator(name = "applets_user_seq_gen", sequenceName = "applets_user_id_seq",allocationSize = 1)
private Integer id;
/** 昵称 */
@Column(length = 65,columnDefinition = "varchar(65)")
private String nickName;
/** 手机号 */
@Column(length = 65,columnDefinition = "varchar(65)")
private String userPhone;
/** 用户头像 */
private String userPhoto;
/** 性别 */
@Column(length = 55,columnDefinition = "varchar(55)")
private String userSex;
/** openid */
private String openId;
/** sessionKey */
private String sessionKey;
private Integer code;
private String token;
private LocalDateTime lastEntryTime;
@Column(columnDefinition = "varchar(64)")
private String lastEntryIp;
public AppletsUser() {
}
public AppletsUser(Integer id, String nickName, String userPhone, String userPhoto, String userSex, String openId, String sessionKey, Integer code, String token) {
this.id = id;
this.nickName = nickName;
this.userPhone = userPhone;
this.userPhoto = userPhoto;
this.userSex = userSex;
this.openId = openId;
this.sessionKey = sessionKey;
this.code = code;
this.token = token;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getUserPhone() {
return userPhone;
}
public void setUserPhone(String userPhone) {
this.userPhone = userPhone;
}
public String getUserPhoto() {
return userPhoto;
}
public void setUserPhoto(String userPhoto) {
this.userPhoto = userPhoto;
}
public String getUserSex() {
return userSex;
}
public void setUserSex(String userSex) {
this.userSex = userSex;
}
public String getOpenId() {
return openId;
}
public void setOpenId(String openId) {
this.openId = openId;
}
public String getSessionKey() {
return sessionKey;
}
public void setSessionKey(String sessionKey) {
this.sessionKey = sessionKey;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
@Override
public String toString() {
return "AppletsUser{" +
"id=" + id +
", nickName='" + nickName + '\'' +
", userPhone='" + userPhone + '\'' +
", userPhoto='" + userPhoto + '\'' +
", userSex='" + userSex + '\'' +
", openId='" + openId + '\'' +
", sessionKey='" + sessionKey + '\'' +
", code=" + code +
", token='" + token + '\'' +
'}';
}
}

View File

@ -71,6 +71,9 @@ public class SysUserEntity implements Serializable {
@Column(columnDefinition = "varchar(64)") @Column(columnDefinition = "varchar(64)")
private String lastEntryIp; private String lastEntryIp;
@Column(columnDefinition = "varchar(88)")
private String openId;
public Integer getId() { public Integer getId() {
return id; return id;
} }
@ -183,6 +186,14 @@ public class SysUserEntity implements Serializable {
this.lastEntryIp = lastEntryIp; this.lastEntryIp = lastEntryIp;
} }
public String getOpenId() {
return openId;
}
public void setOpenId(String openId) {
this.openId = openId;
}
@Override @Override
public String toString() { public String toString() {
return "SysUserEntity{" + return "SysUserEntity{" +
@ -200,6 +211,7 @@ public class SysUserEntity implements Serializable {
", lastEntryTime=" + lastEntryTime + ", lastEntryTime=" + lastEntryTime +
", deleteFlag=" + deleteFlag + ", deleteFlag=" + deleteFlag +
", lastEntryIp='" + lastEntryIp + '\'' + ", lastEntryIp='" + lastEntryIp + '\'' +
", openId='" + openId + '\'' +
'}'; '}';
} }
} }

View File

@ -0,0 +1,26 @@
package com.xkrs.service;
/**
* @author XinYi Song
*/
public interface AppletsUserService {
/**
* 用户小程序登录
* @param avatarUrl 头像
* @param nickName 昵称
* @param sex 性别
* @param code
* @return
*/
String userLogin(String avatarUrl,String nickName,Integer sex,String code);
/**
* 解析手机号
* @param encryptedData
* @param iv
* @param openId
* @return
*/
String decodePhone(String encryptedData,String iv,String openId);
}

View File

@ -0,0 +1,157 @@
package com.xkrs.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xkrs.common.encapsulation.PromptMessageEnum;
import com.xkrs.common.tool.JwtUtil;
import com.xkrs.dao.AppletsUserDao;
import com.xkrs.model.entity.AppletsUser;
import com.xkrs.service.AppletsUserService;
import com.xkrs.utils.DecodeUtils;
import com.xkrs.utils.HttpClientUtil;
import io.micrometer.core.instrument.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.transaction.Transactional;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import static com.xkrs.common.encapsulation.OutputEncapsulation.outputEncapsulationObject;
/**
* @author HP
*/
@Service
public class AppletsUserServiceImpl implements AppletsUserService {
public static Logger log = LoggerFactory.getLogger(AppletsUserServiceImpl.class);
@Resource
private AppletsUserDao appletsUserDao;
@Resource
private DecodeUtils decodeUtils;
/**
* 用户登录
* @param avatarUrl 头像
* @param nickName 昵称
* @param sex 性别
* @param code wx.login()获取的
* @return
*/
@Transactional(rollbackOn = Exception.class)
@Override
public String userLogin(String avatarUrl, String nickName, Integer sex, String code) {
Locale locale = LocaleContextHolder.getLocale();
try {
log.info("进入微信登录实现方法----------------->");
String sexs = sex + "";
// 创建Httpclient对象
String requestUrl = "https://api.weixin.qq.com/sns/jscode2session";
Map<String, String> requestUrlParam = new HashMap<>();
//小程序appId
requestUrlParam.put("appid","wxcdd789a27dd7dace");
//小程序secret
requestUrlParam.put("secret","78436fda2488c08d4a3744bfd1613265");
//小程序端返回的code
requestUrlParam.put("js_code",code);
//默认参数
requestUrlParam.put("grant_type","authorization_code");
JSONObject jsonObject = JSON.parseObject(HttpClientUtil.doPost(requestUrl, requestUrlParam));
// 解析json
//JSONObject jsonObject = (JSONObject) JSONObject.parse(resultString);
String sessionKeys = jsonObject.get("session_key").toString();
log.info("sessionKey-------"+sessionKeys);
String openId = jsonObject.get("openid").toString();
//校验openId是否有效
if (StringUtils.isBlank(openId) || StringUtils.isBlank(sessionKeys)) {
return outputEncapsulationObject(PromptMessageEnum.PROCESS_FAIL,"用户登陆失败",locale);
}
//以unionId去查是否存在用户,如果存在修改用户信息并返回
AppletsUser appletsUser = appletsUserDao.findByOpenId(openId);
if (appletsUser != null) {
appletsUser.setSessionKey(sessionKeys);
appletsUserDao.updateSessionKey(appletsUser.getId(),sessionKeys);
//设置token
log.info("====="+appletsUser.getId());
String token = JwtUtil.sign(appletsUser.getNickName(), sessionKeys);
Map map = new HashMap(3);
map.put("token",token);
map.put("openId",appletsUser.getOpenId());
map.put("session_key",sessionKeys);
return outputEncapsulationObject(PromptMessageEnum.SUCCESS,map,locale);
}else{
AppletsUser appletsUser1 = new AppletsUser();
appletsUser1.setNickName(nickName);
appletsUser1.setUserPhoto(avatarUrl);
appletsUser1.setUserSex(sexs);
appletsUser1.setOpenId(openId);
appletsUser1.setSessionKey(sessionKeys);
//新增用户信息
if (appletsUserDao.save(appletsUser1) != null) {
log.info("插入用户成功------------>");
System.out.println("拿到userId----------------------->"+appletsUser1.getId());
//设置token
String token = JwtUtil.sign(appletsUser1.getNickName(), appletsUser1.getSessionKey());
Map map = new HashMap(3);
map.put("token",token);
map.put("openId",appletsUser1.getOpenId());
map.put("session_key",appletsUser1.getSessionKey());
return outputEncapsulationObject(PromptMessageEnum.SUCCESS,map,locale);
} else {
return outputEncapsulationObject(PromptMessageEnum.PROCESS_FAIL,"微信登录失败,请稍后重试",locale);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return outputEncapsulationObject(PromptMessageEnum.PROCESS_FAIL,"微信登录失败,请稍后重试",locale);
}
/**
* 解析手机号
* @param encryptedData
* @param iv
* @param openId
* @return
*/
@Transactional(rollbackOn = Exception.class)
@Override
public String decodePhone(String encryptedData, String iv, String openId) {
Locale locale = LocaleContextHolder.getLocale();
log.info("进入解密手机号实现方法------------->");
try {
//通过userId获取用户信息用于解密手机号
AppletsUser appletsUser = appletsUserDao.findByOpenId(openId);
if (null == appletsUser) {
return outputEncapsulationObject(PromptMessageEnum.DATA_WRONG,"用户信息错误,请稍后重试",locale);
}
if (StringUtils.isNotBlank(appletsUser.getUserPhone())) {
//以userId作为redisKey存入redis并设置有效时间为半小时
//redisClient.set(tourismUser.getTourismId(), tourismUser.getUserPhone(), 1800);
return outputEncapsulationObject(PromptMessageEnum.SUCCESS,"获取用户手机号成功",locale);
}
// 用于解密手机号的sessionKey
String sessionKey = appletsUser.getSessionKey();
//解密获得手机号
JSONObject jsonObject = decodeUtils.wxDecrypt(encryptedData, sessionKey, iv);
String phone = jsonObject.getString("phoneNumber");
if (StringUtils.isNotEmpty(phone)) {
log.info("用户手机号为--------->" + phone);
//插入用户手机号
appletsUserDao.updatePhone(openId,phone);
log.info("插入用户手机号成功----------->");
//redisClient.set(tourismUser.getTourismId(), phone, 1800);
return outputEncapsulationObject(PromptMessageEnum.SUCCESS,"获取用户手机号成功",locale);
}
} catch (Exception e) {
e.printStackTrace();
}
return outputEncapsulationObject(PromptMessageEnum.PROCESS_FAIL,"获取手机号失败",locale);
}
}

View File

@ -0,0 +1,82 @@
package com.xkrs.utils;
import com.alibaba.fastjson.JSONObject;
import org.apache.tomcat.util.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.stereotype.Component;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.Security;
/**
* 解密工具类
*
* @Author duyongmeng
* @Time 2020/11/10
*/
@Component
public class DecodeUtils {
// 算法名
public static final String KEY_NAME = "AES";
// 加解密算法/模式/填充方式
// ECB模式只用密钥即可对数据进行加密解密CBC模式需要添加一个iv
public static final String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";
/**
* @param encrypted 目标密文
* @param session_key 会话ID
* @param iv 加密算法的初始向量
*/
public JSONObject wxDecrypt(String encrypted, String session_key, String iv) {
String json;
JSONObject jsonObject = null;
byte[] encrypted64 = Base64.decodeBase64(encrypted);
byte[] key64 = Base64.decodeBase64(session_key);
byte[] iv64 = Base64.decodeBase64(iv);
byte[] data;
try {
init();
json = new String(decrypt(encrypted64, key64, generateIV(iv64)));
jsonObject = JSONObject.parseObject(json);
} catch (Exception e) {
e.printStackTrace();
}
return jsonObject;
}
/**
* 初始化密钥
*/
public static void init() throws Exception {
Security.addProvider(new BouncyCastleProvider());
KeyGenerator.getInstance(KEY_NAME).init(128);
}
/**
* 生成iv
*/
public static AlgorithmParameters generateIV(byte[] iv) throws Exception {
// iv 为一个 16 字节的数组这里采用和 iOS 端一样的构造方法数据全为0
// Arrays.fill(iv, (byte) 0x00);
AlgorithmParameters params = AlgorithmParameters.getInstance(KEY_NAME);
params.init(new IvParameterSpec(iv));
return params;
}
/**
* 生成解密
*/
public static byte[] decrypt(byte[] encryptedData, byte[] keyBytes, AlgorithmParameters iv)
throws Exception {
Key key = new SecretKeySpec(keyBytes, KEY_NAME);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
// 设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, key, iv);
return cipher.doFinal(encryptedData);
}
}

View File

@ -0,0 +1,136 @@
package com.xkrs.utils;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author HP
*/
public class HttpClientUtil {
public static String doGet(String url, Map<String, String> param) {
// 创建Httpclient对象
CloseableHttpClient httpclient = HttpClients.createDefault();
String resultString = "";
CloseableHttpResponse response = null;
try {
// 创建uri
URIBuilder builder = new URIBuilder(url);
if (param != null) {
for (String key : param.keySet()) {
builder.addParameter(key, param.get(key));
}
}
URI uri = builder.build();
// 创建http GET请求
HttpGet httpGet = new HttpGet(uri);
// 执行请求
response = httpclient.execute(httpGet);
// 判断返回状态是否为200
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String doGet(String url) {
return doGet(url, null);
}
public static String doPost(String url, Map<String, String> param) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建参数列表
if (param != null) {
List<NameValuePair> paramList = new ArrayList<>();
for (String key : param.keySet()) {
paramList.add(new BasicNameValuePair(key, param.get(key)));
}
// 模拟表单
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
httpPost.setEntity(entity);
}
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String doPost(String url) {
return doPost(url, null);
}
public static String doPostJson(String url, String json) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建请求内容
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
}