企业微信三方开发(三):网页授权登录
初識微信開發
企業微信三方開發:注冊企業微信服務商
企業微信三方開發(一):回調驗證及重要參數獲取
企業微信三方開發(二):獲取access_token
企業微信三方開發(三):網頁授權登錄
企業微信三方開發(四):發送消息
企業微信三方開發(五):掃碼登錄
目錄
前言一
技術棧及工具
一、OAuth2到底是什么?
1.1、首先要對授權登錄有個正確的概念
1.2、OAuth2.0的授權碼模式
二、網頁授權
2.1、新建uni-app項目
2.2、 構建網頁授權鏈接
三、獲取用戶信息及敏感信息
總結
前言一
企業微信登錄分兩種:網頁授權登錄和掃碼授權登錄。
區別除了字面意思一個需要掃碼一個不需要掃碼外。還一個重要應用上的區別就是網頁授權登錄必須在微信客戶端完成。
此文是關于如何實現網頁授權登錄的。
登錄的整體邏輯分三段,從用戶點擊登錄按鈕開始:
點擊登錄按鈕,訪問OAuth2網頁授權鏈接獲取微信授權code【前端處理】
通過授權code換取用戶成員票據user_ticket在內的用戶信息【后端處理】
通過user_ticket獲取用戶敏感信息【后端處理】
這里OAuth2網頁授權鏈接獲取授權code在前端完成。我們前端使用uni-app框架,其好處是搭建方便、支持跨端、語言為現在很熱門的vue。
微信企業三方開發訪問授權鏈接不支持本地調試,官方意見是自己搭建線上調試平臺,然后通過企業微信訪問調試。這里我就只好用我自己線上的服務器和域名進行開發調試。
技術棧及工具
前端開發框架:uni-app
前端開發工具:HBulider
后端開發框架:spring-boot
后端開發工具: idea
一、OAuth2到底是什么?
在做授權登錄時,無論是微信還企業微信,包括許多其它廠的登錄。我們都會碰到一個概念——OAuth
通常也寫作OAuth2.0,其中2.0是他的版本號
1.1、首先要對授權登錄有個正確的概念
通常說到登錄,就涉及兩方:用戶和應用
比如我在使用微信,那么我就是用戶,微信app就是應用。
登錄邏輯也很簡單:我只需要注冊用戶名及密碼到微信服務器,即可用用戶名和密碼登錄
而授權登錄則涉及三方:用戶,認證服務器 ,應用
比如我想使用CSDN,需要注冊登錄。如果我直接選擇用我的微信賬號登錄,這就是一個授權登錄過程。其中用戶就是我,認證服務器就是微信的服務器,應用則是CSDN
其中微信的授權登錄就是采用的OAuth 2.0授權碼模式
1.2、OAuth2.0的授權碼模式
OAuth是一套授權模式的統稱,其中用的最多的就是授權碼模式。
上圖就是OAuth2.0授權碼模式的流程,我們再結合微信授權登錄CSDN的情形講解一下:
首先我點擊CSDN的微信登錄按鈕,CSDN服務器會構造OAuth2鏈接(具體鏈接參看微信文檔,參數包括CSDN的身份ID,以及重定向URI,這個URI也就是CSDN登錄成功的頁面),進入到微信的授權頁
我在微信的授權頁點擊同意授權,微信會跳轉到重定向URI,也就是CSDN登錄成功頁并附上授權碼
進入登錄成功頁時,CSDN服務器會拿著授權碼以及調用憑證AccessToken(由微信指定接口獲取)向微信服務器獲取我的基本信息。整個授權過程完成。
可以看到OAuth2.0很明顯的優點,CSDN完全不用知道更不需要存儲我的微信賬號和密碼,就能判斷我的微信就是我的微信,即我就是我!
二、網頁授權
明白了OAuth2.0,下面構建OAuth2.0鏈接以及其中的參數也就不需要多講。
2.1、新建uni-app項目
因為正好在用uni-app做小程序,所以這里就選擇了這個框架,具體創建方式直接查看官方教程,這里我們已經創建了一個叫easyou-agency的項目。
這里前端用什么框架不重要,關鍵是需要發送http請求
2.2、 構建網頁授權鏈接
接口文檔
注意參數scope,主要用于設置手動授權還是靜默授權。 區別就是授權時需不需要點擊授權按鈕。
如果選擇手動授權,需給應用配置權限
接下來繼續寫代碼,先新建兩個頁面,index和login
login頁面需要訪問構建的URL:
<template>
<view>
<view>
<form @submit="doLogin">
<button plain="true" class="loginBtn" lang="zh_CN" form-type="submit">登錄</button>
</form>
</view>
</view>
</template>
<script>
export default {
data() {
return {
usercode: '',
password: '',
passwordHidden: true
}
},
onLoad:function(){
},
methods: {
doLogin() {
let _this = this
localStorage.setItem("hasLogin",true)
// 這里放自己的域名
let redirect_uri = encodeURI("www.xxxx.com")
let authorUrl = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=ww8273b1801b97c577&redirect_uri='+redirect_uri+'&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect'
window.location.href = authorUrl
}
}
}
</script>
<style>
.loginBtn{
color: #337bd4!important;
border:1rpx solid #337bd4!important;
font-size: 30rpx;
border-radius: 50rpx;
}
</style>
redirect_uri 用的是我線上的域名,也就是我安裝好的應用的主頁url。在應用詳情里配置:
index頁是打開應用時缺省進入的頁面,需要做三件事:
通過storage判斷是否是登錄狀態,不是回到login頁
再判斷url參數是否帶code
如果帶code說明是網頁授權過來的,將code發給后臺換取成員票據user_ticket和用戶信息
index頁如下:
<template>
<view class="content">
<image class="logo" src="/static/logo.png"></image>
<view class="text-area">
<text class="title">{{title}}</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
title: this.$route.query.code
}
},
onLoad() {
if(localStorage.getItem("hasLogin")=='false'){
uni.redirectTo({
url: '../login/login'
});
}
if(this.$route.query.code){
// 從靜模授權進入首頁
localStorage.setItem("auth_code",this.$route.query.code)
this.getuserinfo3rd()
}
},
methods: {
// 獲取敏感信息
getuserinfo3rd:function(){
uni.request({
url: 'http://tantan.vaiwan.com/login/getuserinfo3rd.do?code='+this.$route.query.code,
method:'POST',
success: function (res) {
console.log("res:"+res);
}
})
}
}
}
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
</style>
自此前端部分就完成了,生成發行版本并上傳至自己服務器,并配置好域名指向index。
企業微信能夠成功訪問index,由于storage的登錄狀態為false,所以跳轉到login頁面
企業微信網頁應用有前端調試插件,安裝方法查看官方教程
點擊登錄按鍵:
成功跳轉到首頁
請求鏈接攜帶code,說明構建網頁授權鏈接成功!
如果是手動授權,則會先跳轉到授權頁:
此時就要手動點即授權按鈕授權
三、獲取用戶信息及敏感信息
在1.2中我們在index頁中有個getuserinfo3rd函數向后臺發送ajax請求,參數攜帶了網頁授權成功的code。此時我們就要響應并通過code獲取用戶票據user_ticket,再通過user_ticket獲取用戶敏感信息。
新建一個LoginController:
package com.tan.cwp.controller;
import com.tan.cwp.util.HttpHelper;
import com.tan.cwp.util.PropertiesUtil;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@RestController
@RequestMapping("/login")
public class LoginController {
/**
* 獲取訪問用戶userid
*
* 請求地址:https://qyapi.weixin.qq.com/cgi-bin/service/getuserinfo3rd?suite_access_token=SUITE_ACCESS_TOKEN&code=CODE
* 請求方式: GET
* @parm1 SUITE_ACCESS_TOKEN
* @parm2 oauth2授權成功返回的code
*/
@RequestMapping(value = "getuserinfo3rd.do" ,method = RequestMethod.POST)
public static void getUserInfo3rd(HttpServletRequest request,HttpServletResponse response) throws IOException, JSONException {
String code = request.getParameter("code");
String suite_access_token = PropertiesUtil.getProperty("suite_access_token");
String url = "https://qyapi.weixin.qq.com/cgi-bin/service/getuserinfo3rd?suite_access_token="+suite_access_token+"&code="+code;
JSONObject jsonObj = HttpHelper.doGet(url);
JSONObject result = getUserDetail3rd((String) jsonObj.get("user_ticket"));
// 將信息發送至前臺
PrintWriter out = response.getWriter();
out.print(result);
out.close();
}
/**
* 獲取訪問用戶敏感信息
*
* 請求地址:https://qyapi.weixin.qq.com/cgi-bin/service/getuserdetail3rd?suite_access_token=SUITE_ACCESS_TOKEN
* 請求方式: POST
* @parm1 SUITE_ACCESS_TOKEN
* @parm2 getUserInfo3rd獲得的user_ticket
*/
public static JSONObject getUserDetail3rd(String user_ticket) throws JSONException, IOException {
String suite_access_token = PropertiesUtil.getProperty("suite_access_token");
String url = "https://qyapi.weixin.qq.com/cgi-bin/service/getuserdetail3rd?suite_access_token="+suite_access_token;
JSONObject jsonParms = new JSONObject();
jsonParms.put("user_ticket", user_ticket);
JSONObject jsonObj = HttpHelper.doPost(url,jsonParms);
return jsonObj;
}
}
通過兩個函數先后獲取用戶普通信息和敏感信息。
保存并重新運行
回到企業微信點擊登錄按鈕發現報錯了:
一個標標準準的跨域錯誤
這里的調用域名是我線上的域名www.xxxx.com,被調用域名是我本地的 http://tantan.vaiwan.com,所以跨域了。解決跨域通常有兩種思路:
從調用方出發:通過http服務器(nginx或apache)將被調用域名反向代理到同一個域名
從被調用方出發:通過過濾器增加請求頭,允許指定域名可以跨域
我們選擇第二種方法新增過濾器。
新增filter包,加個叫CrosFilter的過濾器:
package com.tan.cwp.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CrosFilter implements javax.servlet.Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse)servletResponse;
// 允許所有域名跨域
res.addHeader("Access-Control-Allow-Origin","*");
res.addHeader("Access-Control-Allow-Methods","GET,POST,PUT,DELETE,OPTIONS");
res.addHeader("Access-Control-Allow-Headers","Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
在 CwpApplication 中將過濾器配上:
package com.tan.cwp;
import com.tan.cwp.filter.CrosFilter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class CwpApplication {
public static void main(String[] args) {
SpringApplication.run(CwpApplication.class, args);
}
@Bean
public FilterRegistrationBean registerFilter() {
FilterRegistrationBean bean = new FilterRegistrationBean();
// 過濾所有訪問
bean.addUrlPatterns("/*");
bean.setFilter(new CrosFilter());
return bean;
}
}
重啟項目,企業微信點擊登錄:
成功獲取到用戶普通信息和敏感信息!
總結
在獲取用戶信息中,有個open_userid返回值。這個是用戶在應用內的唯一標識,需存入數據庫用作用戶登錄的憑證。
總結
以上是生活随笔為你收集整理的企业微信三方开发(三):网页授权登录的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SAP Spartacus centra
- 下一篇: office doc/xls/ppt 和