代码解析
1.前端接口定义 restful风格 但框架并没有运用起来
2.接口实现
(1)加密参数 (随机定义)
(2)接口所需参数配置(微信开放平台网站应用服务申请后的appId和密钥自行配置即可)
(3)生成接口地址和密钥返回给前端
1.获取当前年月日字符串为密钥其中的一部分进行加密
2.字符串替换成配置类中的参数 将参数返回给前端
3.绑定用户微信id或者直接登录成功
(1)前端发送请求到微信开发平台 返回一张二维码 用户授权后前端会返回一个获取 AccessToken的code和我们上一个接口返回的密钥
(2)接口定义接收参数
code:获取AccessToken的参数
state:密钥
(3)接口实现
1.对密钥进行解密
2.获取AccessToken去获取微信用户信息
3.根据微信id去数据库里查询该微信id是否被绑定,如果被绑定直接返回用户信息登录成功,没有绑定的话(返回微信用户唯一id(这里返回偷了一点懒 没有定义返回对象 直接把微信id返回在里我们用户表的备注字段 这里有和前端说明))
(4)微信未绑定用户进行绑定
1.接口定义
2.参数接收
3.效验账号密码是否正确,然后进行绑定
Java代码
1.微信扫码二维码接口代码
@ApiOperation("请求CODE(PC)")
@PostMapping("/getWeChatQrCode/{geanre}")
public String getWeChatQrCode(@PathVariable int geanre){
try {
Map<String,Object>result=usersService.getWeChatQrCode();
return VikiClient.create(true, "获取成功",result).toString();
}catch (Exception e){
e.printStackTrace();
return VikiClient.create(false, "获取失败").toString();
}
}
2.加密对象
public class Constanst {
/**
* 自定义加密措施
*/
public static final String PWD_MD5 = "Sxz#@";
}
3.时间对象这里就不展示了 就是网上找的时间工具类
4.加密工具类
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
/**
*
* @author lc
* @date 2021/5/15
*/
public class AESUtil {
public static final String PASS_WORD = "9921d7d2e937422aa190794ade95c69c";
/**
* 加密
*
* @param content 需要加密的内容
* @return
*/
public static byte[] encrypt(String content) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(PASS_WORD.getBytes()));
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");// 创建密码器
byte[] byteContent = content.getBytes(StandardCharsets.UTF_8);
cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
byte[] result = cipher.doFinal(byteContent);
return result; // 加密
} catch (NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException e) {
e.printStackTrace();
}
return null;
}
/**解密
* @param content 待解密内容
* @return
*/
public static byte[] decrypt(byte[] content) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(PASS_WORD.getBytes()));
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");// 创建密码器
cipher.init(Cipher.DECRYPT_MODE, key);// 初始化
byte[] result = cipher.doFinal(content);
return result; // 加密
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
}
return null;
}
/**将二进制转换成16进制
* @param buf
* @return
*/
public static String parseByte2HexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
/**将16进制转换为二进制
* @param hexStr
* @return
*/
public static byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1) {
return null;
}
byte[] result = new byte[hexStr.length()/2];
for (int i = 0;i< hexStr.length()/2; i++) {
int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);
int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);
result[i] = (byte) (high * 16 + low);
}
return result;
}
}
5.微信接口地址配置类(appId和secret记得输入还有回调地址定义成你们扫码登录成功后要跳转到的地址)
import java.io.Serializable;
public class HttpParame implements Serializable {
/**
* 应用唯一标识
*/
public static final String APPID = "";
/**
* 密钥
*/
public static final String SECRET = "";
/**
* 微信用户唯一标识
*/
public static final String OPENID = "openid";
/**
* 接口调用凭证
*/
public static final String ACCESS_TOKEN = "access_token";
/**
* 回调地址
*/
public static final String REDIRECT_URL = "";
/**
* 网页授权回调地址
*/
public static final String AUTHORIZATION_URL = "https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect";
/**
* 通过code获取access_token
*/
public static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
/**
* 此接口用于获取用户个人信息 UnionID机制
*/
public static final String GET_UNIONID_URL = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";
}
6.微信二维码接口实现代码
public Map<String,Object> getWeChatQrCode() {
String content = Constanst.PWD_MD5 + DateUtils.getNow(DateUtils.DATE_PATTERN);
//加密
byte[] encryptResult = AESUtil.encrypt(content);
assert encryptResult != null;
String encryptResultStr = AESUtil.parseByte2HexStr(encryptResult);
String url = HttpParame.AUTHORIZATION_URL;
url = url.replaceAll("APPID",HttpParame.APPID);
try {
url = url.replaceAll("REDIRECT_URI", URLEncoder.encode(HttpParame.REDIRECT_URL, "utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
url = url.replaceAll("SCOPE", "snsapi_login");
url = url.replaceAll("STATE", encryptResultStr);
//返回地址
Map<String, Object> map = new HashMap<>(16);
map.put("url", url);
map.put("state", encryptResultStr);
return map;
}
7.根据微信平台扫码完成后返回的code和我们上一个接口返回的密钥state去请求微信平台的AccessToken和用户个人信息
(1)请求对象
import lombok.Data;
import java.io.Serializable;
@Data
public class WeiXinConnectRequest implements Serializable {
private String code;
private String state;
}
(2)接口定义(
注意
:没有生成token的业务删除代码就行)
@ApiOperation("根据code和state获取微信用户信息(PC)")
@PostMapping("/weiXinConnect/{geanre}")
public String weiXinConnect(@PathVariable int geanre,@RequestBody WeiXinConnectRequest request){
try {
AdminUsers users=usersService.weiXinConnect(request);
//type等于0代表扫码登录成功 1代表需要绑定账号
if (users!=null) {
Map<String, Object> result = new HashMap<>(16);
if (users.getId()==null) {
//用户未绑定账号
String wxId = users.getRemark();
//将wxId返回
result.put("wxId", wxId);
result.put("type", "1");
return VikiClient.create(true, "该微信还未绑定用户信息!",result).toString();
}else{
//生成token 存入redis
String token = UUID.randomUUID().toString().replace("-", "");
redisTemplate.opsForValue().set(token, users, 500, TimeUnit.MINUTES);
result.put("token", token);
result.put("id", users.getId());
result.put("type", "0");
result.put("headPortrait", users.getHeadPortrait());
}
return VikiClient.create(true, "登陆成功",result).toString();
}else{
return VikiClient.create(false, "参数不能为空").toString();
}
}catch (RuntimeException e) {
return VikiClient.create(false, e.getMessage(),new ArrayList<>()).toString();
}catch (Exception e){
e.printStackTrace();
return VikiClient.create(false, "登陆失败").toString();
}
}
(3)接口实现
public AdminUsers weiXinConnect(WeiXinConnectRequest request) {
String access_token = null;
String openid = null;
if (request.getCode()!=null &request.getState()!=null) {
//解密
byte[] decryptFrom = AESUtil.parseHexStr2Byte(request.getState());
byte[] decryptResult =AESUtil.decrypt(decryptFrom);
assert decryptResult != null;
String s = new String(decryptResult);
if (!s.equals(Constanst.PWD_MD5+DateUtils.getNow(DateUtils.DATE_PATTERN))) {
throw new RuntimeException("登录失败!请联系管理员!");
}
AccessToken accessToken= this.getAccessToken(request.getCode());
if (accessToken!=null) {
access_token = accessToken.getAccess_token();
openid = accessToken.getOpenid();
if (access_token==null||openid==null) {
throw new RuntimeException("code已经过期请重新获取!");
}
}
//获取微信用户信息
WeiXinUserMessage weiXinUserMessage= this.getUserUnionID(access_token, openid);
if (weiXinUserMessage!=null) {
QueryWrapper<AdminUsers> wrapper = new QueryWrapper<>();
wrapper.eq("wx_id", weiXinUserMessage.getOpenid());
AdminUsers adminUsers = usersDao.selectOne(wrapper);
if (adminUsers==null) {
//如果用户未绑定账号
AdminUsers adminUsers1 = new AdminUsers();
//将Openid放入对象备注字段返回
adminUsers1.setRemark(openid);
return adminUsers1;
}else{
adminUsers.setHeadPortrait(weiXinUserMessage.getHeadimgurl());
return adminUsers;
}
}
}
return null;
}
(4)获取AccessToken
private AccessToken getAccessToken(String code) {
String accessTokenUrl = HttpParame.ACCESS_TOKEN_URL;
accessTokenUrl=accessTokenUrl.replaceAll("APPID",HttpParame.APPID);
accessTokenUrl=accessTokenUrl.replaceAll("SECRET",HttpParame.SECRET);
accessTokenUrl=accessTokenUrl.replaceAll("CODE",code);
String content = null;
try {
content = HttpUrlConnectionUtil.get(accessTokenUrl);
} catch (IOException e) {
e.printStackTrace();
}
if (content==null||"".equals(content)) {
return null;
}else{
JSONObject jsonObject = JSONObject.parseObject(content);
return JSONObject.toJavaObject(jsonObject, AccessToken.class);
}
}
(5)获取微信个人信息
private WeiXinUserMessage getUserUnionID(String access_token, String openid) {
String unionidUrl = HttpParame.GET_UNIONID_URL;
unionidUrl=unionidUrl.replaceAll("ACCESS_TOKEN",access_token);
unionidUrl=unionidUrl.replaceAll("OPENID",openid);
String content = null;
try {
content = HttpUrlConnectionUtil.get(unionidUrl);
} catch (IOException e) {
e.printStackTrace();
}
if (content==null||"".equals(content)) {
return null;
}else{
JSONObject jsonObject = JSONObject.parseObject(content);
return JSONObject.toJavaObject(jsonObject, WeiXinUserMessage.class);
}
}
8.未绑定微信id绑定用户信息
(1)请求参数
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* @author liuchang
*/
@Data
public class AppWeChatLoginRequest implements Serializable {
@ApiModelProperty("微信id")
private String wxId;
@ApiModelProperty("机构简称")
private String orgCode;
@ApiModelProperty("登录名")
private String loginName;
@ApiModelProperty("登录密码")
private String loginPwd;
}
(2)接口定义(
注意
:没有生成token的业务删除代码就行)
@ApiOperation(value="绑定微信id(pc)")
@PostMapping("/loginWithWeChatId/{geanre}")
public String loginWithWeChatId(@PathVariable int geanre,@RequestBody AppWeChatLoginRequest appWeChatLoginRequest){
try {
_AdminUsers adminUsers = usersService.loginWithWeChatId(appWeChatLoginRequest);
//生成token 存入redis
String token = UUID.randomUUID().toString().replace("-", "");
redisTemplate.opsForValue().set(token, adminUsers, 500, TimeUnit.MINUTES);
Map<String, Object> result = new HashMap<>(16);
result.put("token", token);
result.put("id", adminUsers.getId());
result.put("type", "0");
return VikiClient.create(true, "登陆成功",result).toString();
} catch (RuntimeException e) {
return VikiClient.create(false, e.getMessage()).toString();
}catch (Exception e) {
return VikiClient.create(false, "登陆失败").toString();
}
}
(3)接口实现
public _AdminUsers loginWithWeChatId(AppWeChatLoginRequest appWeChatLoginRequest) {
//根据用户机构简称和登录名判断用户是否存在
_AdminUsers adminUsers = adminUsersDao.selectByLoginNameAndOrgCode(appWeChatLoginRequest.getLoginName(), appWeChatLoginRequest.getOrgCode());
if (appWeChatLoginRequest.getWxId()==null||"".equals(appWeChatLoginRequest.getWxId())) {
throw new RuntimeException("微信id不能为空!");
}else{
QueryWrapper<AdminUsers> wrapper = new QueryWrapper<>();
wrapper.eq("wx_id", appWeChatLoginRequest.getWxId());
AdminUsers users = usersDao.selectOne(wrapper);
if (users!=null) {
throw new RuntimeException("该微信已经绑定过用户信息!");
}
}
if (adminUsers==null) {
throw new RuntimeException("用户登录名不存在!");
}else{
//效验密码是否正确
try {
boolean result = VikiMD5Util.validPassword(appWeChatLoginRequest.getLoginPwd(), adminUsers.getLoginPwd());
if (result) {
//绑定微信id
if (adminUsers.getWxId()!=null&&!"".equals(adminUsers.getWxId())){
throw new RuntimeException("该用户已经绑定过微信无需重复绑定!");
}
adminUsers.setWxId(appWeChatLoginRequest.getWxId());
adminUsersDao.updateById(adminUsers);
}else{
throw new RuntimeException("密码不正确,请重新输入!");
}
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
e.printStackTrace();
}
}
adminUsers.setLoginPwd(null);
return adminUsers;
}
这里需要注意我们密码解密工具类自己封装的 换成自己公司的密码解密方式即可
http请求工具类
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Random;
public class HttpUrlConnectionUtil {
public static String post(String urlStr,String params) throws IOException{
URL url=new URL(urlStr);
HttpURLConnection conn=(HttpURLConnection) url.openConnection();
conn.setReadTimeout(15000);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Connection", "Keep-Alive");// 维持长连接
conn.setRequestProperty("Charset", "UTF-8");
if(params!=null && !"".equals(params.trim())){
conn.setDoOutput(true);
OutputStream os=conn.getOutputStream();
os.write(params.getBytes());
os.flush();
os.close();
}
InputStream is=conn.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(is,"utf-8"));
StringBuilder result=new StringBuilder("");
String text=null;
while((text=br.readLine())!=null){
result.append(text);
}
if(is!=null){
is.close();
}
conn.disconnect();
return result.toString();
}
public static String post(String urlStr,String params,String cookie) throws IOException{
URL url=new URL(urlStr);
HttpURLConnection conn=(HttpURLConnection) url.openConnection();
conn.setReadTimeout(999999999);
if(cookie!=null && !cookie.trim().equals("")){
conn.addRequestProperty("Cookie", cookie);
}
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Connection", "Keep-Alive");// 维持长连接
conn.setRequestProperty("Charset", "utf-8");
if(params!=null && !"".equals(params.trim())){
conn.setDoOutput(true);
OutputStream os=conn.getOutputStream();
os.write(params.getBytes());
os.flush();
os.close();
}
InputStream is=conn.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(is,"gb2312"));
StringBuilder result=new StringBuilder("");
String text=null;
while((text=br.readLine())!=null){
result.append(text);
}
if(is!=null){
is.close();
}
conn.disconnect();
return result.toString();
}
public static String get(String urlStr) throws IOException{
URL url=new URL(urlStr);
HttpURLConnection conn=(HttpURLConnection) url.openConnection();
conn.setReadTimeout(15000);
conn.setRequestMethod("GET");
conn.setRequestProperty("Connection", "Keep-Alive");// 维持长连接
conn.setRequestProperty("Charset", "UTF-8");
InputStream is=conn.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(is,"utf-8"));
StringBuilder result=new StringBuilder("");
String text=null;
while((text=br.readLine())!=null){
result.append(text);
}
if(is!=null){
is.close();
}
conn.disconnect();
return result.toString();
}
public String get(String urlStr, String cookie) throws IOException{
Random random = new Random();
String ip = random.nextInt(255)+"."+random.nextInt(255)+"."+random.nextInt(255)+"."+random.nextInt(255);
URL url=new URL(urlStr);
HttpURLConnection conn=(HttpURLConnection) url.openConnection();
conn.setReadTimeout(999999999);
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Connection", "Keep-Alive");// 维持长连接
conn.setRequestProperty("Charset", "utf-8");
conn.setRequestProperty("x-forwarded-for",ip);
if(cookie!=null && !cookie.trim().equals("")){
conn.addRequestProperty("Cookie", cookie);
}
InputStream is=conn.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(is,"GBK"));
StringBuilder result=new StringBuilder("");
String text=null;
while((text=br.readLine())!=null){
result.append(text);
}
if(is!=null){
is.close();
}
conn.disconnect();
return result.toString();
}
}