<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.10.0</version>
</dependency>
@PostMapping("loginByCode")
public ResultData loginByCode(@RequestBody WxLoginVo wxLoginVo){
return ResultData.ok200().setData(wxUserService.loginByCode(wxLoginVo));
}
/**
* 微信登录
*/
WxUser loginByCode(WxLoginVo wxLoginVo);
package com.youruan.examine.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.youruan.examine.config.enums.AuthorizeEnum;
import com.youruan.examine.config.enums.CommonEnum;
import com.youruan.examine.config.excetion.ServiceException;
import com.youruan.examine.config.global.Mark;
import com.youruan.examine.entity.WxUser;
import com.youruan.examine.entity.vo.WxLoginVo;
import com.youruan.examine.entity.vo.WxUserVo;
import com.youruan.examine.mapper.WxUserMapper;
import com.youruan.examine.service.WxUserService;
import com.youruan.examine.util.HttpUtil;
import com.youruan.examine.util.WxUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@Transactional
@Service
public class WxUserServiceImpl implements WxUserService {
private Logger logger = LoggerFactory.getLogger(WxUserServiceImpl.class);
@Autowired
WxUserMapper userMapper;
@Value("${examine.wxchat.appId}")
private String appId;
@Value("${examine.wxchat.secret}")
private String secret;
@Value("${examine.wxchat.grantType}")
private String grantType;
/**
* 微信登录
* @param loginVo
* @return
*/
@Override
public WxUser loginByCode(WxLoginVo loginVo) {
logger.info("loginByCode进入============="+loginVo.toString());
WxUser resVo = new WxUser();
if (StringUtils.isBlank(loginVo.getCode())) {
throw new ServiceException("授权码不能为空");
}
WxUserVo wxUserVo = new WxUserVo();
try {
Map<String, Object> wxResult = getOpenIdByHttpClient(loginVo.getCode());
logger.info(wxResult.toString());
String sessionkey = "";
if (wxResult.containsKey("session_key")) {
sessionkey = wxResult.get("session_key").toString();
}
String openid = "";
if (wxResult.containsKey("openid")) {
openid = wxResult.get("openid").toString();
logger.info(openid);
List<WxUser> userList = listByOppenId(openid);
/**
* 如果已经注册过了 直接返回数据
*/
if (userList.size()>0) {
BeanUtils.copyProperties(userList.get(0),resVo);
return resVo;
}
}
if (StringUtils.isBlank(loginVo.getEncryptedData()) || StringUtils.isBlank(loginVo.getIv())) {
throw new ServiceException("加密的数据不能为空");
}
if (StringUtils.isBlank(sessionkey)) {
throw new ServiceException("授权信息有误");
}
JSONObject userInfo = WxUtil.getUserInfo(loginVo.getEncryptedData(), sessionkey, loginVo.getIv());
logger.info(userInfo.toJSONString());
wxUserVo = JSONObject.toJavaObject(userInfo, WxUserVo.class);
} catch (IOException e) {
e.printStackTrace();
}
loginVo.setSourceType(AuthorizeEnum.WX.getCode());
changeUserInfo(loginVo, wxUserVo);
//注册账号
WxUser user = registerUserByLoginVo(loginVo);
BeanUtils.copyProperties(user,resVo);
return resVo;
}
public Map<String, Object> getOpenIdByHttpClient(String code) throws IOException {
HttpUtil httpUtil = new HttpUtil();
String url = Mark.WX_OPENID_URL;
String result = httpUtil.header("X-Requested-With", "XMLHttpRequest")
.data("appid", appId)
.data("secret", secret)
.data("js_code", code)
.data("grant_type", grantType)
.url(url)
.get();
Map<String, Object> map = JSONObject.parseObject(result, Map.class);
if (!map.containsKey("openid")) {
throw new ServiceException("错误信息:" + map.get("errmsg"));
}
return map;
}
public List<WxUser> listByOppenId(String oppenId){
Example example = new Example(WxUser.class);
example.createCriteria().andEqualTo("oppenId",oppenId);
return userMapper.selectByExample(example);
}
private void changeUserInfo(WxLoginVo loginVo, WxUserVo userInfoVo) {
loginVo.setUnionId(userInfoVo.getUnionId());
loginVo.setOpenId(userInfoVo.getOpenId());
loginVo.setAvatar(userInfoVo.getAvatarUrl());
loginVo.setNickName(userInfoVo.getNickName());
loginVo.setSex(userInfoVo.getGender());
}
/**
* 注册(微信登录)
*
* @param loginVo
* @return
*/
private WxUser registerUserByLoginVo(WxLoginVo loginVo){
//注册用户
WxUser user = new WxUser();
user.setCreateDate(new Date());
user.setOppenId(loginVo.getOpenId());
user.setUsername(loginVo.getNickName());
user.setHeadImg(loginVo.getAvatar());
user.setSex(loginVo.getSex());
user.setUserType(CommonEnum.APPLETS.getCode());
user.setPersonNum(UUID.randomUUID().toString().replaceAll("-", ""));
userMapper.insertSelective(user);
/*String codePath = "";
try {
codePath = createUserOrTicketCode(JSON.toJSONString(user));
}catch (Exception e){
throw new ServiceException("二维码生成异常");
}
user.setQrcode(codePath);
userMapper.updateByPrimaryKeySelective(user);*/
/*//绑定微信
AuthorizeInfo info = new AuthorizeInfo();
info.setUserId(user.getId());
info.setSex(loginVo.getSex());
info.setOpenType(AuthorizeEnum.WX.getCode());
info.setOpenId(loginVo.getOpenId());
info.setNickName(loginVo.getNickName());
info.setAvatar(loginVo.getAvatar());
info.setCreateTime(new Date());
authorizeInfoMapper.insertSelective(info);*/
return user;
}
}
package com.youruan.examine.entity.vo;
public class WxLoginVo {
/**
* 授权码
*/
private String code;
/**
* 加密的数据
*/
private String encryptedData;
/**
* 加密偏移量
*/
private String iv;
/**
* 昵称
*/
private String nickName;
/**
* 头像
*/
private String avatar;
/**
* 性别
*/
private String sex;
/**
* 微信openId
*/
private String openId;
/**
* 微信unionId
*/
private String unionId;
/**
* 来源 01-微信 02-qq
*/
private String sourceType;
/**
* 手机号
*/
private String mobile;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getOpenId() {
return openId;
}
public void setOpenId(String openId) {
this.openId = openId;
}
public String getUnionId() {
return unionId;
}
public void setUnionId(String unionId) {
this.unionId = unionId;
}
public String getEncryptedData() {
return encryptedData;
}
public void setEncryptedData(String encryptedData) {
this.encryptedData = encryptedData;
}
public String getIv() {
return iv;
}
public void setIv(String iv) {
this.iv = iv;
}
public String getSourceType() {
return sourceType;
}
public void setSourceType(String sourceType) {
this.sourceType = sourceType;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
}
package com.youruan.examine.entity.vo;
public class WxUserVo {
/**
* openId
*/
private String openId;
/**
* 昵称
*/
private String nickName;
/**
* 性别
*/
private String gender;
/**
* 头像
*/
private String avatarUrl;
/**
* unionId
*/
private String unionId;
public String getOpenId() {
return openId;
}
public void setOpenId(String openId) {
this.openId = openId;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getAvatarUrl() {
return avatarUrl;
}
public void setAvatarUrl(String avatarUrl) {
this.avatarUrl = avatarUrl;
}
public String getUnionId() {
return unionId;
}
public void setUnionId(String unionId) {
this.unionId = unionId;
}
}
package com.youruan.examine.util;
import okhttp3.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
/**
* 说明: http请求客户端
* @Author zhangsanfeng
*
* new HttpUtil().url(url)
* .data("token", "76AF8B201FEC")
* .data("body", json.toJSONString())
* .post();
*
* new HttpUtil().url(url)
* .data("token", "76AF8B201FEC")
* .file("image", fileRootPath)
* .postMultipart();
*/
public class HttpUtil {
private final static int CONNECT_TIMEOUT = 5000; // in milliseconds
private final static String DEFAULT_ENCODING = "UTF-8";
private static Logger log = LoggerFactory.getLogger(HttpUtil.class);
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
private static final MediaType MEDIA_TYPE_JPG = MediaType.parse("image/jpg");
private OkHttpClient.Builder httpClient = new OkHttpClient().newBuilder().connectTimeout(10, TimeUnit.SECONDS).readTimeout(60, TimeUnit.SECONDS).writeTimeout(30, TimeUnit.SECONDS);
private Request.Builder requestBuilder = new Request.Builder();
private FormBody.Builder formBodyBuilder = new FormBody.Builder();
private MultipartBody.Builder multipartBodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
private String url = "";
private Map<String, String> headers = new LinkedHashMap<>();
private Map<String, String> queryParas = new LinkedHashMap<>();
private Map<String, String> files = new LinkedHashMap<>();
public HttpUtil url(String url) {
this.url = url;
return this;
}
public HttpUtil data(String key, String value) {
this.queryParas.put(key, value);
return this;
}
public HttpUtil files(Map<String, String> files) {
this.files.putAll(files);
return this;
}
public HttpUtil file(String key, String value) {
this.files.put(key, value);
return this;
}
public HttpUtil header(String key, String value) {
this.headers.put(key, value);
return this;
}
public HttpUtil userAgent(String userAgent) {
header("User-Agent", userAgent);
return this;
}
public HttpUtil contentType(String contentType) {
header("Content-Type", contentType);
return this;
}
public HttpUtil acceptEncoding(String acceptEncoding) {
header("Accept-Encoding", acceptEncoding);
return this;
}
/**
* Send POST request
*
* @throws IOException
*/
public String post() throws IOException {
if (StringUtils.isEmpty(url)) {
log.error("URL为空");
return "";
}
for (Entry<String, String> entry : queryParas.entrySet()) {
formBodyBuilder.add(entry.getKey(), entry.getValue());
}
requestBuilder.url(url);
for (Entry<String, String> entry : headers.entrySet()) {
requestBuilder.addHeader(entry.getKey(), entry.getValue());
}
Request request = requestBuilder.post(formBodyBuilder.build()).build();
Response response = httpClient.build().newCall(request).execute();
if (!response.isSuccessful()) {
throw new IOException("POST请求服务器端错误: " + response);
}
return response.body().string();
}
/**
* Send GET request
*
* @throws IOException
*/
public String get() throws IOException {
if (StringUtils.isEmpty(url)) {
log.error("URL为空");
return "";
}
requestBuilder.url(buildUrlWithQueryString(url, queryParas));
for (Entry<String, String> entry : headers.entrySet()) {
requestBuilder.addHeader(entry.getKey(), entry.getValue());
}
Request request = requestBuilder.build();
Response response = httpClient.build().newCall(request).execute();
if (!response.isSuccessful()) {
throw new IOException("GET请求服务器端错误: " + response);
}
return response.body().string();
}
/**
* Send POST request
*
* @throws IOException
*/
public String postMultipart() throws IOException {
if (StringUtils.isEmpty(url)) {
log.error("URL为空");
return "";
}
for (Entry<String, String> entry : files.entrySet()) {
File file = new File(entry.getValue());
if (file.exists()) {
multipartBodyBuilder.addFormDataPart(entry.getKey(), file.getName(), RequestBody.create(MEDIA_TYPE_PNG, file));
} else {
log.warn("待上传文件没有在磁盘上找到, filePath = " + entry.getValue());
}
}
for (Entry<String, String> entry : queryParas.entrySet()) {
multipartBodyBuilder.addFormDataPart(entry.getKey(), entry.getValue());
}
requestBuilder.url(url);
for (Entry<String, String> entry : headers.entrySet()) {
requestBuilder.addHeader(entry.getKey(), entry.getValue());
}
Request request = requestBuilder.post(multipartBodyBuilder.build()).build();
Response response = httpClient.build().newCall(request).execute();
if (!response.isSuccessful()) {
throw new IOException("服务器端错误: url=" + this.url + ", response=" + response.toString());
}
return response.body().string();
}
/**
* Build queryString of the url
*/
private static String buildUrlWithQueryString(String url, Map<String, String> queryParas) {
if (queryParas == null || queryParas.isEmpty()) {
return url;
}
StringBuffer sb = new StringBuffer(url);
boolean isFirst;
if (!url.contains("?")) {
isFirst = true;
sb.append("?");
} else {
isFirst = false;
}
for (Entry<String, String> entry : queryParas.entrySet()) {
if (isFirst) {
isFirst = false;
} else {
sb.append("&");
}
String key = entry.getKey();
String value = entry.getValue();
if (StringUtils.isEmpty(value)) {
try {
value = URLEncoder.encode(value, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
sb.append(key).append("=").append(value);
}
return sb.toString();
}
//发送响应流方法
public static void setResponseHeader(HttpServletResponse response, String fileName) {
try {
try {
fileName = new String(fileName.getBytes(), "ISO8859-1");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
response.setContentType("application/octet-stream;charset=ISO8859-1");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
response.addHeader("Pargam", "no-cache");
response.addHeader("Cache-Control", "no-cache");
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static String postData(String urlStr, String data) {
return postData(urlStr, data, null);
}
public static String postData(String urlStr, String data, String contentType) {
BufferedReader reader = null;
try {
URL url = new URL(urlStr);
URLConnection conn = url.openConnection();
conn.setDoOutput(true);
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setReadTimeout(CONNECT_TIMEOUT);
if (contentType != null)
conn.setRequestProperty("content-type", contentType);
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING);
if (data == null)
data = "";
writer.write(data);
writer.flush();
writer.close();
reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line);
sb.append("\r\n");
}
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (reader != null)
reader.close();
} catch (IOException e) {
}
}
return null;
}
}
package com.youruan.examine.util;
import com.alibaba.fastjson.JSONObject;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;
public class WxUtil {
public static JSONObject getUserInfo(String encryptedData, String sessionKey, String iv) {
// 被加密的数据
byte[] dataByte = Base64.decode(encryptedData);
// 加密秘钥
byte[] keyByte = Base64.decode(sessionKey);
// 偏移量
byte[] ivByte = Base64.decode(iv);
try {
// 如果密钥不足16位,那么就补足. 这个if 中的内容很重要
int base = 16;
if (keyByte.length % base != 0) {
int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
byte[] temp = new byte[groups * base];
Arrays.fill(temp, (byte) 0);
System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
keyByte = temp;
}
// 初始化
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
parameters.init(new IvParameterSpec(ivByte));
cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
byte[] resultByte = cipher.doFinal(dataByte);
if (null != resultByte && resultByte.length > 0) {
String result = new String(resultByte, "UTF-8");
return JSONObject.parseObject(result);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
package com.youruan.examine.config.excetion;
public class ServiceException extends RuntimeException {
private static final long serialVersionUID = -250798675069124299L;
private int errorCode;
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
public int getErrorCode() {
return errorCode;
}
public ServiceException(int errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
public ServiceException(String message) {
super(message);
}
public ServiceException(String message, Throwable cause) {
super(message, cause);
}
public ServiceException(int errorCode, String message, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
}
}
package com.youruan.examine.config.enums;
public enum CommonEnum {
APPLETS("01","小程序用户"),
ORGANIZER("02","主办方");
private String code;
private String desc;
CommonEnum(String code, String desc){
this.code = code;
this.desc = desc;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
package com.youruan.examine.config.enums;
public enum AuthorizeEnum {
WX("01","微信"),
QQ("02","QQ");
private String code;
private String desc;
AuthorizeEnum(String code, String desc){
this.code = code;
this.desc = desc;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
Mark.java
/**
* 微信获取openId的url
*/
public static final String WX_OPENID_URL = "https://api.weixin.qq.com/sns/jscode2session";
examine:
wxchat:
appId: wx
secret: f7
grantType: authorization_code