|
@@ -0,0 +1,216 @@
|
|
|
|
+package com.szwl.controller;
|
|
|
|
+
|
|
|
|
+import com.szwl.model.bo.ResponseModel;
|
|
|
|
+import com.szwl.model.bo.UserDetailBO;
|
|
|
|
+import com.szwl.model.entity.TAdmin;
|
|
|
|
+import com.szwl.model.entity.TWechat;
|
|
|
|
+import com.szwl.model.utils.HttpClientUtils;
|
|
|
|
+import com.szwl.service.TAdminService;
|
|
|
|
+import com.szwl.service.TWechatService;
|
|
|
|
+import io.swagger.annotations.Api;
|
|
|
|
+import io.swagger.annotations.ApiOperation;
|
|
|
|
+import org.json.JSONObject;
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
|
+import org.springframework.web.bind.annotation.*;
|
|
|
|
+
|
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
|
+import java.io.IOException;
|
|
|
|
+import java.io.UnsupportedEncodingException;
|
|
|
|
+import java.net.URLEncoder;
|
|
|
|
+import java.security.MessageDigest;
|
|
|
|
+import java.util.*;
|
|
|
|
+
|
|
|
|
+@Api(value = "/WxLoginController", tags = {"微信登录接口"})
|
|
|
|
+@RestController
|
|
|
|
+@RequestMapping("/wxLogin")
|
|
|
|
+public class WxLoginController {
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private TAdminService tAdminService;
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private TWechatService tWechatService;
|
|
|
|
+
|
|
|
|
+ // 从 yml 文件中获取
|
|
|
|
+ @Value("${oauth.wx.appid}")
|
|
|
|
+ private String appid;
|
|
|
|
+
|
|
|
|
+ @Value("${oauth.wx.appsecret}")
|
|
|
|
+ private String appsecret;
|
|
|
|
+
|
|
|
|
+ @Value("${oauth.callback.http}")
|
|
|
|
+ private String http;
|
|
|
|
+
|
|
|
|
+ @ApiOperation(value = "用户默认授权")
|
|
|
|
+ @CrossOrigin(value = "https://open.weixin.qq.com/")
|
|
|
|
+ @GetMapping("/menuOauth")
|
|
|
|
+ public void menuOauth(HttpServletResponse response) throws IOException {
|
|
|
|
+ String path = http + "/wxLogin/callback?";
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ path = URLEncoder.encode(path, "UTF-8");
|
|
|
|
+ } catch (UnsupportedEncodingException e) {
|
|
|
|
+ throw new RuntimeException(e);
|
|
|
|
+ }
|
|
|
|
+ // 第一步:用户同意授权,获取code
|
|
|
|
+ // 静默授权,只能获取用户openid
|
|
|
|
+ String url = "https://open.weixin.qq.com/connect/oauth2/authorize?"
|
|
|
|
+ + "appid=" + appid
|
|
|
|
+ + "&redirect_uri=" + path
|
|
|
|
+ + "&response_type=code"
|
|
|
|
+ + "&scope=snsapi_base"
|
|
|
|
+ + "&state=STATE" +
|
|
|
|
+ "#wechat_redirect";
|
|
|
|
+ response.sendRedirect(url);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @ApiOperation(value = "微信登录回调")
|
|
|
|
+ @CrossOrigin(value = "https://api.weixin.qq.com/")
|
|
|
|
+ @GetMapping("/callback")
|
|
|
|
+ public void callback(HttpServletRequest request) throws IOException {
|
|
|
|
+ // code 只能用一次,5分钟过期
|
|
|
|
+ String code = request.getParameter("code");
|
|
|
|
+
|
|
|
|
+ // 第二步:通过code换取网页授权access_token
|
|
|
|
+ String url = "https://api.weixin.qq.com/sns/oauth2/access_token?" +
|
|
|
|
+ "appid=" + appid +
|
|
|
|
+ "&secret=" + appsecret +
|
|
|
|
+ "&code=" + code +
|
|
|
|
+ "&grant_type=authorization_code";
|
|
|
|
+ JSONObject jsonObject = HttpClientUtils.get(url);
|
|
|
|
+
|
|
|
|
+ String openid = jsonObject.getString("openid");
|
|
|
|
+ String accessToken = jsonObject.getString("access_token");
|
|
|
|
+
|
|
|
|
+ // snsapi_base式的网页授权流程即到此为止。
|
|
|
|
+ // 根据 openid 去查找已绑定的用户信息
|
|
|
|
+ TWechat tWechat = tWechatService.getById(openid);
|
|
|
|
+ if (Objects.nonNull(tWechat)) {
|
|
|
|
+ String adminId = tWechat.getAdminId();
|
|
|
|
+ TAdmin tAdmin = tAdminService.getById(adminId);
|
|
|
|
+ String username = tAdmin.getUsername();
|
|
|
|
+ String password = tAdmin.getPassword();
|
|
|
|
+ // 然后根据 username 和 password 去登录
|
|
|
|
+ TAdminController tAdminController = new TAdminController();
|
|
|
|
+ ResponseModel<UserDetailBO> login = tAdminController.login(username, password);
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+ // 说明未绑定
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 前往微信开发平台申请,根据 code 获取 openid
|
|
|
|
+ * @param code
|
|
|
|
+ * @return
|
|
|
|
+ * @throws IOException
|
|
|
|
+ */
|
|
|
|
+ @CrossOrigin(value = "https://api.weixin.qq.com/")
|
|
|
|
+ private String getOpenidByCode(String code) throws IOException {
|
|
|
|
+
|
|
|
|
+ String grantType = "authorization_code";
|
|
|
|
+
|
|
|
|
+ // 拼接请求地址
|
|
|
|
+ String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?" +
|
|
|
|
+ "appid=" + appid +
|
|
|
|
+ "&secret=" + appsecret +
|
|
|
|
+ "&code=" + code +
|
|
|
|
+ "&grant_type=" + grantType;
|
|
|
|
+
|
|
|
|
+ JSONObject jsonObject = HttpClientUtils.get(requestUrl);
|
|
|
|
+ String accessToken = jsonObject.getString("access_token");
|
|
|
|
+ String openId = jsonObject.getString("openid");
|
|
|
|
+
|
|
|
|
+ // 拉取用户信息(需scope为 snsapi_userinfo)
|
|
|
|
+// String userinfoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN";
|
|
|
|
+// JSONObject object = HttpClientUtils.get(userinfoUrl);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// String s = JSON.toJSONString(object);
|
|
|
|
+ return openId;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @ApiOperation(value = "获取微信签名")
|
|
|
|
+ @GetMapping("/wx/signature")
|
|
|
|
+ public Map<String, String> getSignature(@RequestParam String url) throws IOException {
|
|
|
|
+ HashMap<String, String> signatureMap = new HashMap<>();
|
|
|
|
+
|
|
|
|
+ String nonceStr = UUID.randomUUID().toString();
|
|
|
|
+ String timestamp = Long.toString(System.currentTimeMillis() / 1000);
|
|
|
|
+ String jsapiTicket = getJsapiTicket();
|
|
|
|
+ String signature = "";
|
|
|
|
+
|
|
|
|
+ // 按照微信官方要求构造含有 nonceStr、timestamp、jsapi_ticket 和 url 的字符串
|
|
|
|
+ String rawString = "jsapi_ticket=" + jsapiTicket +
|
|
|
|
+ "&noncestr=" + nonceStr +
|
|
|
|
+ "×tamp=" + timestamp +
|
|
|
|
+ "&url=" + url;
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ MessageDigest crypt = MessageDigest.getInstance("SHA-1");
|
|
|
|
+ crypt.reset();
|
|
|
|
+ crypt.update(rawString.getBytes("UTF-8"));
|
|
|
|
+ signature = byteToHex(crypt.digest());
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ e.printStackTrace();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ signatureMap.put("signature", signature);
|
|
|
|
+ signatureMap.put("appId", appid);
|
|
|
|
+ signatureMap.put("timestamp", timestamp);
|
|
|
|
+ signatureMap.put("nonceStr", nonceStr);
|
|
|
|
+
|
|
|
|
+ return signatureMap;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取授权页ticket ticket 有效期,一般为 7200 秒
|
|
|
|
+ * @return
|
|
|
|
+ * @throws IOException
|
|
|
|
+ */
|
|
|
|
+ @CrossOrigin(value = "https://api.weixin.qq.com/")
|
|
|
|
+ private String getJsapiTicket() throws IOException {
|
|
|
|
+ // 获取 jsapi_ticket 的逻辑,向微信服务器发送 GET 请求
|
|
|
|
+ String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket"
|
|
|
|
+ + "?access_token=" + getAccessToken() // 先获取 access_token
|
|
|
|
+ + "&type=jsapi"; // 指定获取 jsapi_ticket
|
|
|
|
+
|
|
|
|
+ JSONObject jsonObject = HttpClientUtils.get(url);
|
|
|
|
+ String ticket = jsonObject.getString("ticket");
|
|
|
|
+ // 返回获取到的 jsapi_ticket
|
|
|
|
+ return ticket;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取接口调用凭据 token 有效期: 7200s
|
|
|
|
+ * @return
|
|
|
|
+ * @throws IOException
|
|
|
|
+ */
|
|
|
|
+ @CrossOrigin(value = "https://api.weixin.qq.com/")
|
|
|
|
+ private String getAccessToken() throws IOException {
|
|
|
|
+ // 构造 GET 请求,获取 access_token
|
|
|
|
+ String url = "https://api.weixin.qq.com/cgi-bin/token"
|
|
|
|
+ + "?grant_type=client_credential"
|
|
|
|
+ + "&appid=" + appid
|
|
|
|
+ + "&secret=" + appsecret;
|
|
|
|
+
|
|
|
|
+ JSONObject jsonObject = HttpClientUtils.get(url);
|
|
|
|
+ return jsonObject.getString("access_token");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private static String byteToHex(final byte[] hash) {
|
|
|
|
+ Formatter formatter = new Formatter();
|
|
|
|
+ for (byte b : hash) {
|
|
|
|
+ formatter.format("%02x", b);
|
|
|
|
+ }
|
|
|
|
+ String result = formatter.toString();
|
|
|
|
+ formatter.close();
|
|
|
|
+ return result;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+}
|