总体逻辑:扫描二维码后在后端根据微信官方提供的API拿到用户openID,在进行判断是否存在该openID,存在返回用户账号密码(加密后)跳转至cas登录页面使用js触发自动登录,不存在跳转至绑定页面。
开发过程中细节问题:
1、cas不要过度的改动,不要破坏前人的代码逻辑,尽量只修改自己的二维码login页面和js即可,需要在md5加密部分代码判断是否为md5类型。
2、生成二维码微信和企业微信的默认样式不同,改起来很复杂,且不可上传本地的css文件覆盖,因为微信官方出于安全考虑,只能上传公网文件,或者根据herf参数传入。我使用的方法是herf传输传入。
3、herf参数传入应该为经过base64在线编码后的代码,使用css写法无法写入该地方。
4、二维码注意细节:(1)获取二维码并设置回调地址(必须与微信回调域一致,否则二维码会报错)(2)通过回调接口得到授权临时票据code(3)通过临时票据调用微信接口获取access_token、openid(用户微信唯一标识)(4)通过access_token、openid调用微信接口获取用户信息(5)完成注册登录业务。
效果图如下:
部分核心代码:
(1)获取微信openID工具类
主要作用调微信的API
package com.xk.wechatlogin.util;
/**
* @version : 1.0
* @Author : WuShiWen
*/
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import java.util.Map;
/**
* description: WeChatUtils 微信获取用户工具类<br>
*
* @date: 2021/8/19 0019 上午 10:05 <br>
* @author: William <br>
* version: 1.0 <br>
*/
public class WeChatUtils {
/**
* 获取微信accessToken路径
*/
private static final String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
/**
* 微信appId
*/
private static final String APP_ID = "wxa5fb23212134f3af7";
/**
* 微信appSecret
*/
private static final String APP_SECRET = "ad32346645fffsdff643434067105f755f";
/**
* description: getAccessToken 根据code获取accessToken<br>
* version: 1.0 <br>
* @date: 2021/8/19 0019 上午 10:10 <br>
* @author: William <br>
* @param code 微信用户授权code
* @return java.util.Map<java.lang.String,java.lang.String>
*/
public static Map<String,String> getAccessToken(String code){
return getAccessToken(code,APP_ID,APP_SECRET);
}
/**
* description: getAccessToken 根据code获取微信用户信息,返回map如果正确map包含access_token ,如果错误则包含:errcode<br>
* version: 1.0 <br>
* @date: 2021/8/19 0019 上午 10:11 <br>
* @author: William <br>
* @param code 微信授权code
* @param appId 微信appId
* @param appSecret 微信appSecret
* @return java.util.Map<java.lang.String,java.lang.String>
*/
public static Map<String,String> getAccessToken(String code,String appId,String appSecret){
//判断所有字段不能为空
if(isAnyBlank(code,appId,appSecret)){
throw new IllegalArgumentException("参数错误");
}
String requestUrl = GET_ACCESS_TOKEN_URL.replace("APPID",appId)
.replace("SECRET",appSecret).replace("CODE",code);
String result = HttpUtil.get(requestUrl);
return JsonUtils.parseMap(result, String.class, String.class);
}
/**
* description: isAnyBlank 判断是否存在空字符串,Hutool未编写<br>
* version: 1.0 <br>
* @date: 2021/8/19 0019 上午 10:25 <br>
* @author: William <br>
* @param strs 字符串
* @return java.lang.Boolean
*/
public static Boolean isAnyBlank(CharSequence... strs){
//如果为空直接返回true
if (ArrayUtil.isEmpty(strs)) {
return true;
}
for (CharSequence str : strs) {
if (StrUtil.isBlank(str)) {
return true;
}
}
return false;
}
}
jsonutils作用解析json字符串
也可以使用JSON解析库来解析JSON数据,如:org.json、Gson、Jackson等
package com.xk.wechatlogin.util;
/**
* @version : 1.0
* @Author : WuShiWen
*/
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* description: json工具类 <br>
* version: 1.0 <br>
* @date: 2019/7/20 0019 上午 10:17 <br>
* @author: William <br>
*/
public class JsonUtils {
public static final ObjectMapper MAPPER = new ObjectMapper();
private static final Logger logger = LoggerFactory.getLogger(JsonUtils.class);
public static String serialize(Object obj) {
if (obj == null) {
return null;
}
if (obj.getClass() == String.class) {
return (String) obj;
}
try {
return MAPPER.writeValueAsString(obj);
} catch (JsonProcessingException e) {
logger.error("json序列化出错:" + obj, e);
return null;
}
}
public static <T> T parse(String json, Class<T> tClass) {
try {
return MAPPER.readValue(json, tClass);
} catch (IOException e) {
logger.error("json解析出错:" + json, e);
return null;
}
}
public static <E> List<E> parseList(String json, Class<E> eClass) {
try {
return MAPPER.readValue(json, MAPPER.getTypeFactory().constructCollectionType(List.class, eClass));
} catch (IOException e) {
logger.error("json解析出错:" + json, e);
return null;
}
}
public static <K, V> Map<K, V> parseMap(String json, Class<K> kClass, Class<V> vClass) {
try {
return MAPPER.readValue(json, MAPPER.getTypeFactory().constructMapType(Map.class, kClass, vClass));
} catch (IOException e) {
logger.error("json解析出错:" + json, e);
return null;
}
}
public static <T> T nativeRead(String json, TypeReference<T> type) {
try {
return MAPPER.readValue(json, type);
} catch (IOException e) {
logger.error("json解析出错:" + json, e);
return null;
}
}
}
企业微信解析openID工具类
package com.xk.wechatlogin.util;
/**
* @version : 1.0
* @Author : WuShiWen
*/
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* description: WeChatUtils 微信获取用户工具类<br>
*
* @date: 2021/8/19 0019 上午 10:05 <br>
* @author: William <br>
* version: 1.0 <br>
*/
public class QYWeChatUtils {
public static String getAccessToken() {
String appId = "ww3122222222101a";//appid企业ID
String secret = "5e22222222ushkj2234lhgrncqSZ0QtLFDdo";//secret为PC门户扫码登录测试的secret,后续根据实际应用secret修改
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=" + appId + "&corpsecret=" + secret;//企业微信提供的API接口,不可修改
try {
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("GET");
int responseCode = con.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuilder content = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
content.append(inputLine);
}
in.close();
System.out.println("content====="+content.toString());
// 解析返回的JSON数据,提取access_token
String jsonResponse = content.toString();
System.out.println("jsonResponse====="+jsonResponse);
// 这里需要使用JSON解析库来解析JSON数据,如:org.json、Gson、Jackson等
String accessToken = parseAccessToken(jsonResponse);
return accessToken;
} else {
System.out.println("HTTP request failed. Response Code: " + responseCode);
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static String parseAccessToken(String jsonResponse) {
// 在这里使用JSON解析库来解析JSON数据并提取出access_token的值
// 例如,使用org.json库
JSONObject jsonObject = new JSONObject(jsonResponse);
String accessToken = jsonObject.getString("access_token");
return accessToken;
}
public static String getOpenId(String accessToken, String code) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=" + accessToken + "&code=" + code;
try {
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("GET");
int responseCode = con.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuilder content = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
content.append(inputLine);
}
in.close();
// 解析返回的JSON数据,提取openID
String jsonResponse = content.toString();
// 这里需要使用JSON解析库来解析JSON数据,如:org.json、Gson、Jackson等
String openId = parseOpenId(jsonResponse);
return openId;
} else {
System.out.println("HTTP request failed. Response Code: " + responseCode);
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static String parseOpenId(String jsonResponse) {
// 在这里使用JSON解析库来解析JSON数据并提取出openID的值
// 例如,使用org.json库
JSONObject jsonObject = new JSONObject(jsonResponse);
String openId = jsonObject.getString("UserId");
return openId;
}
}
二维码的HTML代码:
<script>
function weChatLogin() {
// 创建微信登录的对象
new WxLogin({
self_redirect: false, // 是否在iframe内跳转 redirect_uri
id: 'code', // 希望二维码嵌入容器的 id
appid: 'wx1111111dd734f3af7',
scope: 'snsapi_login',//应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可
redirect_uri: encodeURIComponent("http://127.0.0.1:9019/wechatlogin"),
//如果引入外部css链接,微信处于安全考虑必须引入公网的链接,或者下面这种写法,通过base64在线编辑后引入
href: `data:text/css;base64,Lmlt.........`//此出根据实际样式经过base64编码后填入,前缀不要改
})
}
</script>
<script>
//授权回调域:192.168.3.151
//appid (企业ID)企业微信应用的唯一标识符,用于区分不同的企业微信应用,是用于区分不同的企业微信应用.
//agentid (应用ID)是企业微信应用的一个属性,是用于区分同一个企业中的不同应用。
function qyWeChatLogin(){
window.WwLogin({
"id" : "code2", //显示二维码的容器id
"appid" : "ww35111111312a",
"agentid" : "1111111", //企业微信的cropID,在 企业微信管理端->我的企业 中查看
"redirect_uri" :"http://127.0.0.1:9019/QYWechatlogin", //重定向地址,需要进行UrlEncode
"state" : "3828293919281", //用于保持请求和回调的状态,授权请求后原样带回给企业。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议企业带上该参数
"href" : `data:text/css;base64,Lmlt...........`//此出根据实际样式经过base64编码后填入,前缀不要改
});}
</script>
截取url中的参数
function getQueryString(name) {
var reg = new RegExp("(^|&)" + encodeURIComponent(name) + "=([^&]*)(&|$)", "i");
var r = window.location.search.substr(1).match(reg);
if (r !== null) {
return decodeURIComponent(r[2]);
}
return null;
}
// 调用方法
console.log("userid",getQueryString( "userId" ))
// alert(getQueryString( "userid" ));
// alert(getQueryString( "password" ));
$("#username").val(getQueryString( "userId" ));
$("#password").val(getQueryString( "password"));
if($("#username").val()!==""&&$("#username").val()!==null){
$("#fm1").submit();
}