1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 微信开放平台开发第三方授权登陆:微信扫码登录

微信开放平台开发第三方授权登陆:微信扫码登录

时间:2020-01-29 01:00:16

相关推荐

微信开放平台开发第三方授权登陆:微信扫码登录

一、概述

根据需求,需要拥有第三方微信登录功能,并获取到用户信息。

网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统。

二、前期准备工作

1、注册邮箱账号。

2、根据邮箱账号注册微信开放平台账号,完善开发者资料。

3、申请开发者资质认证、填写相关资料、填写发票、支付认证金额。提交并等待认证结果

1)申请开发者资质认证2)选定类型3)填写“认证资料”4)填写“管理员信息”5)上传“企业基本信息”材料:6)进入填写发票及支付费用

4、认证成功后,创建网站应用,填写基本信息、下载网站信息登记表填写并上传扫描件、填写授权回调域等。提交审核等待结果。

1)创建网站应用2)创建移动应用

5、认证成功后,创建移动应用,至少选择安卓、IOS、WP8其中一种平台

6、创建应用成功后,申请微信登陆,等待审核结果,待审核通过后,可进行微信登陆的开发。

注:创建应用和开发者资质认证可同时进行

准备工作大致流程图

三、开发流程

1)第三方发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到第三方网站,并且带上授权临时票据code参数;

2)通过code参数加上AppID和AppSecret等,通过API换取access_token;

3)通过access_token进行接口调用,获取用户基本数据资源或帮助用户实现基本操作。

四、具体实现步骤

1、准备工作

1)添加依赖

<!-- 添加httpclient支持 --><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.2</version></dependency><dependency><groupId>net.sf.json-lib</groupId><artifactId>json-lib</artifactId><version>2.4</version><classifier>jdk15</classifier></dependency>

2)配置文件

## 微信开放平台wechat:open:# APPIDappid: ******# APPSECRETappsecret: ******# 回调地址redirect_uri:

3)实体类

/**** @author: xxm* 功能描述: access_token封装基础类* @date: /2/2 10:41*/@Datapublic class Token {private String openid; //授权用户唯一标识private String accessToken; //接口调用凭证private Integer ExpiresIn; //access_token接口调用凭证超时时间,单位(秒)}

/**** @author: xxm* 功能描述: 微信与网站绑定关系表* @date: /2/1 17:08*/@Data@Table(name = "UserWeChat")@NameStyle(Style.normal)public class UserWeChat implements Serializable {private static final long serialVersionUID = 8997358443007506192L;@Id@GeneratedValue(generator = "JDBC")private Integer id;//用户idprivate Integer userId;//微信OpenIdprivate String openId;//昵称private String nickName;}

/**** @author: xxm* 功能描述: access_token封装基础类* @date: /2/2 10:41*/@Datapublic class Token {private String openid; //授权用户唯一标识private String accessToken; //接口调用凭证private Integer ExpiresIn; //access_token接口调用凭证超时时间,单位(秒)}

4)微信工具类

/**** @author: xxm* 功能描述: 微信登录相关接口工具类* @date: /2/23 16:14* @param: * @return: */public class WeChatCommonUtil {private static Logger log = LoggerFactory.getLogger(WeChatCommonUtil.class);/*** @author: xxm* 功能描述: urlEncodeUTF8工具类* (用于将扫描二维码后重定向的资源url进行编码)* @date: /2/22 15:55* @param:* @return:*/public static String urlEncodeUTF8(String source) {String result = source;try {result = .URLEncoder.encode(source, "utf-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}return result;}/**** @author: xxm* 功能描述: 获取openid等信息的方法* @date: /2/22 17:52* @param: * @return: */public static Token getTokenWithOpenid(String appid, String appsecret, String code) {String findAccessTokenUrl = "https://api./sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";Token token = null;// 发起GET请求获取凭证String requestUrl = findAccessTokenUrl.replace("APPID", appid).replace("SECRET", appsecret).replace("CODE", code);JSONObject jsonObject = JSONObject.fromObject(httpsRequest(requestUrl, "GET", null));if (null != jsonObject) {try {token = new Token();token.setOpenid(jsonObject.getString("openid"));token.setAccessToken(jsonObject.getString("access_token"));token.setExpiresIn(jsonObject.getInt("expires_in"));} catch (JSONException e) {token = null;// 获取token失败log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));}}return token;}/**** @author: xxm* 功能描述: 根据openid获取用户信息的方法* @date: /2/22 17:52* @param: * @return: */public static WechatUserInfo getUserinfo(String access_token, String openid) {WechatUserInfo wxuse = new WechatUserInfo();String findUseinfo = "https://api./sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";String requestUrl = findUseinfo.replace("ACCESS_TOKEN", access_token).replace("OPENID", openid);JSONObject jsonObject = JSONObject.fromObject(httpsRequest(requestUrl, "GET", null));if (null != jsonObject) {try {wxuse.setNickname(jsonObject.getString("nickname"));wxuse.setHeadimgurl(jsonObject.getString("headimgurl"));wxuse.setUnionid(jsonObject.getString("unionid"));wxuse.setOpenid(jsonObject.getString("openid"));} catch (JSONException e) {e.printStackTrace();}}return wxuse;}/*** 发送https请求** @param requestUrl 请求地址* @param requestMethod 请求方式(GET、POST)* @param outputStr 提交的数据* @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)* 返回微信服务器响应的信息*/public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {try {// 创建SSLContext对象,并使用我们指定的信任管理器初始化TrustManager[] tm = {new MyX509TrustManager() };SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");sslContext.init(null, tm, new java.security.SecureRandom());// 从上述SSLContext对象中得到SSLSocketFactory对象SSLSocketFactory ssf = sslContext.getSocketFactory();URL url = new URL(requestUrl);HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();conn.setSSLSocketFactory(ssf);conn.setDoOutput(true);conn.setDoInput(true);conn.setUseCaches(false);// 设置请求方式(GET/POST)conn.setRequestMethod(requestMethod);conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");// 当outputStr不为null时向输出流写数据if (null != outputStr) {OutputStream outputStream = conn.getOutputStream();// 注意编码格式outputStream.write(outputStr.getBytes("UTF-8"));outputStream.close();}// 从输入流读取返回内容InputStream inputStream = conn.getInputStream();InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");BufferedReader bufferedReader = new BufferedReader(inputStreamReader);String str = null;StringBuffer buffer = new StringBuffer();while ((str = bufferedReader.readLine()) != null) {buffer.append(str);}// 释放资源bufferedReader.close();inputStreamReader.close();inputStream.close();conn.disconnect();return buffer.toString();} catch (ConnectException ce) {log.error("连接超时:{}", ce);} catch (Exception e) {log.error("https请求异常:{}", e);}return null;}}

/*** @author: xxm* @description:信任管理器* @date: /2/22 17:41*/public class MyX509TrustManager implements X509TrustManager {// 检查客户端证书@Overridepublic void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}// 检查服务器端证书@Overridepublic void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}// 返回受信任的X509证书数组@Overridepublic X509Certificate[] getAcceptedIssuers() {return null;}}

2、请求获取Code

前提:应用已经获取相应的网页授权作用域(scope=snsapi_login)

开发:第三方网站引导用户打开链接

https://open./connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

参数说明:

注意:若提示“该链接无法访问”,请检查参数是否填写错误,如redirect_uri的域名与审核时填写的授权域名不一致或scope不为snsapi_login。

/**** @author: xxm* 功能描述: 跳转至登录授权页面(页面出现二维码)* @date: /2/22 17:50* @param: * @return: */@RequestMapping("/login")public String openWeChatLogin() {// 防止csrf攻击(跨站请求伪造攻击)String state = UUID.randomUUID().toString().replaceAll("-", "");String url = "https://open./connect/qrconnect?" +"appid=" +env.getProperty("wechat.open.appid").trim() +"&redirect_uri=" +WeChatCommonUtil.urlEncodeUTF8(env.getProperty("wechat.open.redirect_uri").trim()+"/wechat/weChatLogin_epf") +"&response_type=code" +"&scope=snsapi_login" +// 由后台自动生成"&state=" + state +"#wechat_redirect";return "redirect:" + url;}

3、用户同意授权与否

用户允许授权后,将会重定向到redirect_uri的网址上,并且带上code和state参数

redirect_uri?code=CODE&state=STATE

若用户禁止授权,则不会重定向到我们提供的回调地址中

成功授权后,将获得Code,通过Code可以获取access_token

4、获取access_token

通过上述方法获取的code获取access_token.

Http Get请求

https://api./sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

参数说明:

请求后,

返回成功的json串为(样例):

{

“access_token”:“ACCESS_TOKEN”, // 接口调用凭证

“expires_in”:7200, // access_token接口调用凭证超时时间,单位(秒)

“refresh_token”:“REFRESH_TOKEN”, //用户刷新access_token

“openid”:“OPENID”, //授权用户唯一标识

“scope”:“SCOPE”, //用户授权的作用域,使用逗号(,)分隔

“unionid”: “o6_bmasdasdsad6_2sgVt7hMZOPfL” //当且仅当该网站应用已获得该用户的userinfo授权时,才会出现该字段

}

失败返回的样例:

{“errcode”:40029,“errmsg”:“invalid code”}

失败可能原因: 暂时不明

Token tokenWithOpenid = WeChatCommonUtil.getTokenWithOpenid(loginAppid, loginSecrect, code);

5、通过access_token调用接口获取用户个人信息(UnionID机制)

前提:

access_token有效且未超时;

微信用户已授权给第三方应用帐号相应接口作用域(scope)。

Http Get请求:

https://api./sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID

参数

返回成功的json结果(样例):

{

“openid”:“OPENID”, //普通用户的标识,对当前开发者帐号唯一

“nickname”:“NICKNAME”, //普通用户昵称

“sex”:1, //普通用户性别,1为男性,2为女性

“province”:“PROVINCE”, //普通用户个人资料填写的省份

“city”:“CITY”, //普通用户个人资料填写的城市

“country”:“COUNTRY”, //国家,如中国为CN

“headimgurl”: “/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0”, //用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空

“privilege”:[ //用户特权信息,json数组,如微信沃卡用户为(chinaunicom)

“PRIVILEGE1”,

“PRIVILEGE2”

],

“unionid”: " o6_bmasdasdsad6_2sgVt7hMZOPfL" //用户统一标识。针对一个微信开放平台帐号下的应用,同一用户的unionid是唯一的

}

失败JSON样例:

{“errcode”:40003,“errmsg”:“invalid openid”}

注意:在用户修改微信头像后,旧的微信头像URL将会失效,因此开发者应该自己在获取用户信息后,将头像图片保存下来,避免微信头像URL失效后的异常情况

最好保存用户unionID信息,以便以后在不同应用中进行用户信息互通。

//通过access_token调用接口WechatUserInfo wxuse = WeChatCommonUtil.getUserinfo(access_token, openid);

6、授权成功后台处理

获取到access_token,在通过access_token获取到微信用户信息WechatUserInfo,就可以判断微信账号是否已经与网站账号关联 ,如果关联了,则此处可以直接登录跳转到主页,但在此处,我还是用了Spring Security安全框架,所有需要微信登录需要经过Spring Security的url请求进行验证,才可以登录。

如果微信账号没有关联网站账号,则可以跳转到微信绑定页面,完成微信绑定。

/**** @author: xxm* 功能描述: 授权成功后* @date: /2/22 17:50* @param:* @return:*/@ResponseBody@RequestMapping("/weChatLogin_epf")public ModelAndView weChatLogin_epf(HttpServletRequest request, HttpSession session, RedirectAttributes attribute){ModelAndView model = new ModelAndView();//获取到code,这个code应该是微信那边定义的String code = request.getParameter("code");//第三方网站(即我们自己)自定义的参数,可以存储一些重要信息和防伪信息,因为这个接口是完全暴露的,所以必须要有防伪措施,防止恶意请求来搞事String state=request.getParameter("state");//如果这两个字段都为空值,就可以判定为而已请求if(StringUtils.isBlank(code)||StringUtils.isBlank(state)){logger.info("非法请求,缺少必要的参数");model.setViewName("redirect:/login");attribute.addFlashAttribute("errorInfo", "非法请求,缺少必要的参数!");attribute.addFlashAttribute("success", false);return model;}logger.info("获取到的code是 :" + code+",state="+state);//通过code获取access_tokenString loginAppid = env.getProperty("wechat.open.appid").trim();String loginSecrect = env.getProperty("wechat.open.appsecret").trim();try {Token tokenWithOpenid = WeChatCommonUtil.getTokenWithOpenid(loginAppid, loginSecrect, code);if (null != tokenWithOpenid) {String openid = tokenWithOpenid.getOpenid();String access_token = tokenWithOpenid.getAccessToken();//通过access_token调用接口WechatUserInfo wxuse = WeChatCommonUtil.getUserinfo(access_token, openid);logger.info("微信用户信息:" + wxuse);UserWeChatDto uwcDto = new UserWeChatDto();uwcDto.setOpenId(openid);List<UserWeChatDto> uwcList = userWeChatClient.getListByParam(uwcDto);if (null != uwcList && uwcList.size()==1) {UserWeChatDto userWeChatDto = uwcList.get(0);//微信账号已经与网站账号关联//根据用户id查询用户SysUser currentUser = userControllerClient.getUserById(userWeChatDto.getUserId());session.setAttribute("username", currentUser.getUsername());// 微信登录需要经过的url请求model.setViewName("redirect:/wechat/weChatLogin");model.addObject("openid",openid);attribute.addFlashAttribute("success", true);} else {//微信账号没有关联网站账号String url = "redirect:/weChatBind";model.setViewName(url);attribute.addFlashAttribute("wechatUserInfo", wxuse);attribute.addFlashAttribute("nickname", wxuse.getNickname());attribute.addFlashAttribute("openid", wxuse.getOpenid());}} else {logger.error("获取token失败");model.setViewName("redirect:/login");attribute.addFlashAttribute("errorInfo", "获取token失败!");attribute.addFlashAttribute("success", false);}}catch (Exception e) {e.printStackTrace();model.setViewName("redirect:/login");attribute.addFlashAttribute("errorInfo", "微信授权登录失败!");attribute.addFlashAttribute("success", false);}finally {return model;}}

7、刷新access_token

由于access_token有效期(目前为2个小时)较短,当access_token超时后,可以使用refresh_token进行刷新,access_token刷新结果有两种:

若access_token已超时,那么进行refresh_token会获取一个新的access_token,新的超时时间;若access_token未超时,那么进行refresh_token不会改变access_token,但超时时间会刷新,相当于续期access_token。

refresh_token拥有较长的有效期(30天),当refresh_token失效的后,需要用户重新授权。

请求方法:

获取第一步的code后,请求以下链接进行refresh_token:

https://api./sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN

参数说明:

成功返回的结果:

{

“access_token”:“ACCESS_TOKEN”, //接口调用凭证

“expires_in”:7200, // access_token接口调用凭证超时时间,单位(秒)

“refresh_token”:“REFRESH_TOKEN”, //用户刷新access_token

“openid”:“OPENID”, //授权用户唯一标识

“scope”:“SCOPE” //用户授权的作用域,使用逗号(,)分隔

}

失败样例:

{“errcode”:40030,“errmsg”:“invalid refresh_token”}

注意:

1、Appsecret 是应用接口使用密钥,泄漏后将可能导致应用数据泄漏、应用的用户数据泄漏等高风险后果;存储在客户端,极有可能被恶意窃取(如反编译获取Appsecret);

2、access_token 为用户授权第三方应用发起接口调用的凭证(相当于用户登录态),存储在客户端,可能出现恶意获取access_token 后导致的用户数据泄漏、用户微信相关接口功能被恶意发起等行为;

3、refresh_token 为用户授权第三方应用的长效凭证,仅用于刷新access_token,但泄漏后相当于access_token 泄漏,风险同上。

建议将secret、用户数据(如access_token)放在App云端服务器,由云端中转接口调用请求。

五、测试结果

请自己测试

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。