一、微信官方文檔
https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
二、准備工作
1)一個域名
● 自己備案申請一個域名
● 使用內網穿透工具
2)微信公眾平台測試號配置
① 配置JS接口安全域名(不要帶http)
② 配置網頁授權回調頁面域名(不要帶http)
③ 掃碼關注測試號
3)獲取公眾平台測試號的 AppID 和 AppSecret
三、授權步驟
1.第三方發起微信授權登錄請求,微信用戶允許授權第三方應用后,微信會拉起應用或重定向到第三方網站,並且帶上授權臨時票據 code 參數;
2.通過 code 參數加上 AppID 和 AppSecret 等,通過 API 換取 access_token;
3.通過 access_token 進行接口調用,獲取用戶基本數據資源或幫助用戶實現基本操作。
四、實例
- 項目結構
使用 SpringBoot 構建項目
1)Maven依賴
<!--用於對JSON格式的數據進行解析和打包-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
<!--支持 HTTP 通訊,實現了所有 HTTP 的方法等-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--Thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2)配置application.yaml文件
# 端口號
server:
port: 8080
# 微信公眾號配置
weChat:
appid: xxxxx # 微信測試號ID
appsecret: xxxxxxx # 微信測試號Secret
token: xxxx # Token
backUrl: http://byr1vl4.nat.ipyingshe.com # 你的域名
backApi: /callBack # 登錄回調的API
3)配置攔截器解決跨域問題(如跨域沒問題,可忽略)
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class CrossInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
return true;
}
}
4)公眾平台測試號接口配置(可跳過忽略)
測試號配置
URL:填寫 http+前面添加的域名+校驗API
Token:任意字符串,只要和后端相對應就行
DecriptUtil工具類:SHA1加密
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class DecriptUtil {
/**
* @param decript
* @return
*/
public static String SHA1(String decript) {
try {
MessageDigest digest = MessageDigest
.getInstance("SHA-1");
digest.update(decript.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuffer hexString = new StringBuffer();
// 字節數組轉換為 十六進制 數
for (int i = 0; i < messageDigest.length; i++) {
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
}
接口校驗,接入微信公眾平台
Controller
@Controller
public class WxAuthController {
@Autowired
private WxAuthService wxAuthService;
/**
* 校驗接入微信公眾平台開發
*
* @param signature
* @param timestamp
* @param nonce
* @param echostr
* @return
*/
@GetMapping("/wxSignatureCheck")
@ResponseBody
public String wxSignatureCheck(
@RequestParam(value = "signature") String signature,
@RequestParam(value = "timestamp") String timestamp,
@RequestParam(value = "nonce") String nonce,
@RequestParam(value = "echostr") String echostr) {
return wxAuthService.wxSignatureCheck(signature, timestamp, nonce, echostr);
}
}
Service
import com.cyan.wx_auth_demo.utils.DecriptUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
@Slf4j
@Service
public class WxAuthService {
@Value("${weChat.token}")
private String token;
/**
* 驗證簽名
*
* @param signature
* @param timestamp
* @param nonce
* @param echostr
* @return 校驗成功返回echostr,否則為null
*/
public String wxSignatureCheck(String signature, String timestamp, String nonce, String echostr) {
ArrayList<String> array = new ArrayList<>();
array.add(signature);
array.add(timestamp);
array.add(nonce);
//排序
String sortString = sort(token, timestamp, nonce);
//加密
String mytoken = DecriptUtil.SHA1(sortString);
//校驗簽名
if (mytoken != null && mytoken != "" && mytoken.equals(signature)) {
log.info("簽名校驗通過");
//如果檢驗成功輸出 echostr,微信服務器接收到此輸出,才會確認檢驗完成。
return echostr;
} else {
log.info("簽名校驗失敗");
return null;
}
}
/**
* 排序方法
*
* @param token
* @param timestamp
* @param nonce
* @return
*/
public static String sort(String token, String timestamp, String nonce) {
String[] strArray = {token, timestamp, nonce};
Arrays.sort(strArray);
StringBuilder sbuilder = new StringBuilder();
for (String str : strArray) {
sbuilder.append(str);
}
return sbuilder.toString();
}
}
5)實現微信授權登錄
AuthUtil工具類
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
@Slf4j
public class AuthUtil {
public static JSONObject doGetJson(String url) throws ClientProtocolException, IOException {
JSONObject jsonObject = null;
//創建HttpClient對象,發送請求,獲取數據
CloseableHttpClient httpClient = HttpClients.createDefault();
//創建httpGet方法對象
HttpGet httpGet = new HttpGet(url);
//設置請求頭
httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36");
//發送請求,獲取響應的數據
log.info("請求地址:{}", url);
CloseableHttpResponse response = httpClient.execute(httpGet);
//獲取響應體
HttpEntity entity = response.getEntity();
if (entity != null) {
// 把返回的結果轉換為JSON對象
String result = EntityUtils.toString(entity, "GB2312");
log.info("響應結果:{}", result);
jsonObject = JSONObject.parseObject(result);
}
httpGet.releaseConnection();
return jsonObject;
}
}
網站主頁,用戶點擊登錄按鈕跳轉登錄授權頁面
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IndexController {
/**
* 登錄按鈕
* http://byr1vl4.nat.ipyingshe.com/index
*
* @return
*/
@GetMapping("/index")
public String login() {
return "<a href='http://byr1vl4.nat.ipyingshe.com/authorize'>微信登錄</a>";
}
}
網頁登錄授權控制類
import com.alibaba.fastjson.JSONObject;
import com.cyan.wx_auth_demo.utils.AuthUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
import java.net.URLEncoder;
@Slf4j
@Controller
public class WxAuthController {
@Value("${weChat.appid}")
private String appid;
@Value("${weChat.appsecret}")
private String appsecret;
@Value("${weChat.backUrl}")
private String backUrl;
@Value("${weChat.backApi}")
private String backApi;
/**
* 一、公眾號微信登錄授權
* <p>
* 通過微信接口獲取code
* 1、如果用戶同意授權,頁面將跳轉至 redirect_uri/?code=CODE&state=STATE
* 2、獲取到 code 與 state
*
* @return
*/
@GetMapping("/authorize")
public String authorize() throws IOException {
// 第一步:用戶同意授權,獲取code
String redirectUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appid
+ "&redirect_uri=" + URLEncoder.encode(backUrl + backApi, "UTF-8")
+ "&response_type=code"
+ "&scope=snsapi_userinfo"
+ "&state=STATE#wechat_redirect";
// 重定向地址
System.out.println("重定向至微信登錄授權地址: " + redirectUrl);
return "redirect:" + redirectUrl;
}
/**
* 二、回調獲取用戶信息
*
* @param code 第一步獲取到的code
* @return
* @throws IOException
*/
@GetMapping("/callBack")
@ResponseBody
public String callBack(String code) throws IOException {
// 1、通過code(authorize方法獲取)換取網頁授權access_token
String accessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?"
+ "appid=" + appid
+ "&secret=" + appsecret
+ "&code=" + code
+ "&grant_type=authorization_code";
JSONObject accessTokenObj = AuthUtil.doGetJson(accessTokenUrl);
// 2、拉取用戶信息,傳參 access_token 和 openId
String userinfoUrl = "https://api.weixin.qq.com/sns/userinfo?"
+ "access_token=" + accessTokenObj.getString("access_token")
+ "&openid=" + accessTokenObj.getString("openId")
+ "lang=zh_CN";
JSONObject userInfoObj = AuthUtil.doGetJson(userinfoUrl);
// 將用戶信息返回到前端
StringBuilder sb = new StringBuilder();
sb.append("<div>");
sb.append("<h3>授權成功</h3>");
sb.append("<img alt='頭像' src='" + userInfoObj.getString("headimgurl") + "'>");
sb.append("<p>" + userInfoObj.getString("nickname") + "</p>");
sb.append("<p>" + userInfoObj.getString("province") + "</p>");
sb.append("</div>");
return sb.toString();
}
}
運行結果
① 登錄頁
② 授權頁
② 授權成功頁
到此,微信授權登錄成功,如果有運行問題請自行調試,我這邊能正常運行的。