應(yīng)用場景
企業(yè)網(wǎng)站或者后臺管理系統(tǒng)登錄時营曼,在開放外網(wǎng)使用時通過賬號密碼登錄有賬號盜用的風(fēng)險缭召,因此開發(fā)一個通過企業(yè)微信掃碼登錄的系統(tǒng)能很大程度降低這種風(fēng)險戒悠。
開始
提前準(zhǔn)備好內(nèi)網(wǎng)穿透烦衣,比如ngrok
內(nèi)網(wǎng)穿透的方案有很多嚷闭,可以百度自行查找,使用內(nèi)網(wǎng)穿透主要是由于企業(yè)微信的回調(diào)地址必須是域名论寨,不能是localhost或者ip地址星立,通過內(nèi)網(wǎng)穿透便于開發(fā)階段的調(diào)試工作-
在企業(yè)微信中創(chuàng)建應(yīng)用
1.1 登錄企業(yè)微信
1.2 在應(yīng)用管理中創(chuàng)建應(yīng)用
1.3 配置企業(yè)微信授權(quán)登錄
1.4 設(shè)置企業(yè)微信授權(quán)回調(diào)并在工作臺中隱藏
第一步內(nèi)網(wǎng)穿透映射到本地的域名填在這里
因?yàn)檫@個應(yīng)用的作用就是掃碼登錄,所以這里可以隱藏掉 配置文件
- 在你的程序里配置企業(yè)微信的一些信息
application.yml
wechat:
cropid: *********
agentid: *********
agentsecret: *********
redirect: http://*****.ngrok.io/weworklogin
寫一個bean,方便拿到這些屬性
@Data
@Component
public class WechatWorkBean {
@Value("${wechat.cropid}")
private String cropId;
@Value("${wechat.agentid}")
private String agentId;
@Value("${wechat.agentsecret}")
private String agentSecret;
@Value("${wechat.redirect}")
private String redirect;
}
在你的登錄視圖中加入這些屬性
model.addAttribute("wechat",wechatWorkBean);
- 登錄頁面中加入企業(yè)微信的二維碼
引入js文件
<script th:src="@{js/wechat_work.js}" charset="utf-8"></script>
可以放個div來放二維碼
<div id="qrcode"></div>
加一段js代碼顯示出二維碼來
<script th:inline="javascript">
let wechat = [[${wechat}]];
window.WwLogin({
"id" : "qrcode",
"appid" : wechat.cropId,
"agentid" : wechat.agentId,
"redirect_uri" : wechat.redirect,
"state" : "",
// "href" : "",
});
</script>
值得注意的是redirect_uri的域名要和之前配置的回調(diào)域在同一個域名下
- 掃碼之后的處理
- 和所有微信相關(guān)的開發(fā)一樣葬凳,拿到access_token
access_token的有效時間是兩個小時绰垂,重復(fù)獲取的話,之前的access_token會失效火焰,官方建議的是全局緩存劲装,我把它存在了redis里
@Component
@Slf4j
public class WechatWorkAccessToken {
@Autowired
RedisUtils redisUtils;
@Autowired
WechatWorkBean wechatWorkBean;
public String getWeworkAccessToken() {
String key = "wechatWorkAccessToken_" + wechatWorkBean.getAgentId();
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET";
Object o = redisUtils.get(key);
if(o == null) {
url = url.replace("ID", wechatWorkBean.getCropId()).replace("SECRET", wechatWorkBean.getAgentSecret());
String result = HttpUtils.doGet(url);
log.info(result);
Map<String, Object> map = FastJsonConvert.convertJSONToObject(result, Map.class);
String accessToken = String.valueOf(map.get("access_token"));
String expired = String.valueOf(map.get("expires_in"));
if(StringUtils.isNotBlank(accessToken)) {
redisUtils.set(key,accessToken,RedisUtils.HOUR);
return accessToken;
} else {
log.error(result);
return "";
}
}
return o.toString();
}
}
access_token告一段落,之前配置的redirect_url中要有個處理掃碼之后處理的方法昌简,掃碼之后企業(yè)微信會發(fā)一個重定向占业,后面加個code參數(shù)請求到redirect_url中,這里要寫一下這個方法處理這個纯赎,通過code拿到企業(yè)微信的用戶id谦疾,然后通過用戶id查找到系統(tǒng)用戶,就可以完成登錄了址否。
@RequestMapping("weworklogin")
public String oauth2(HttpServletRequest request, HttpServletResponse response) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=ACCESS_TOKEN&code=CODE";
// 1.檢查code
String code = request.getParameter("code");
// code存在, 獲取用戶信息
if(StringUtils.isNotBlank(code)) {
url = url.replace("ACCESS_TOKEN",wechatWorkAccessToken.getWeworkAccessToken()).replace("CODE",code);
String result = HttpUtils.doGet(url);
Map<String,String> map = FastJsonConvert.convertJSONToObject(result,Map.class);
String userId = map.get("UserId");
if(StringUtils.isNotBlank(userId)) {
String session = null;
try {
session = baseUserService.login(userId);
CookieUtils.addCookie("eubasession",session);
return "redirect:/";
} catch (EuException e) {
return "redirect:login?msg=" + UriEncoder.encode("您的企業(yè)微信ID【"+ userId +"】,請聯(lián)系管理員創(chuàng)建或綁定賬號");
}
} else {
return "redirect:login?msg="+UriEncoder.encode("您不是企業(yè)用戶餐蔬,無法登陸,請聯(lián)系管理員");
}
} else {
return "redirect:login?msg="+UriEncoder.encode("登錄失敗佑附,請聯(lián)系管理員");
}
}
基本上到這里核心功能就做完了。后面可以自行擴(kuò)展一下仗考,我做了一些額外的處理音同,在開發(fā)環(huán)境中還是允許賬號密碼登錄,線上環(huán)境中屏蔽掉秃嗜。权均。