应用场景
企业网站或者后台管理系统登录时,在开放外网使用时通过账号密码登录有账号盗用的风险,因此开发一个通过企业微信扫码登录的系统能很大程度降低这种风险。
开始
提前准备好内网穿透,比如ngrok
内网穿透的方案有很多,可以百度自行查找,使用内网穿透主要是由于企业微信的回调地址必须是域名,不能是localhost或者ip地址,通过内网穿透便于开发阶段的调试工作-
在企业微信中创建应用
1.1 登录企业微信
1.2 在应用管理中创建应用
1.3 配置企业微信授权登录
1.4 设置企业微信授权回调并在工作台中隐藏
第一步内网穿透映射到本地的域名填在这里
因为这个应用的作用就是扫码登录,所以这里可以隐藏掉 配置文件
- 在你的程序里配置企业微信的一些信息
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);
- 登录页面中加入企业微信的二维码
引入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的域名要和之前配置的回调域在同一个域名下
- 扫码之后的处理
- 和所有微信相关的开发一样,拿到access_token
access_token的有效时间是两个小时,重复获取的话,之前的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中要有个处理扫码之后处理的方法,扫码之后企业微信会发一个重定向,后面加个code参数请求到redirect_url中,这里要写一下这个方法处理这个,通过code拿到企业微信的用户id,然后通过用户id查找到系统用户,就可以完成登录了。
@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("您的企业微信ID【"+ userId +"】,请联系管理员创建或绑定账号");
}
} else {
return "redirect:login?msg="+UriEncoder.encode("您不是企业用户,无法登陆,请联系管理员");
}
} else {
return "redirect:login?msg="+UriEncoder.encode("登录失败,请联系管理员");
}
}
基本上到这里核心功能就做完了。后面可以自行扩展一下,我做了一些额外的处理,在开发环境中还是允许账号密码登录,线上环境中屏蔽掉。。