我们经常会访问一些网站,用微信登录的时候需要用到授权,那么微信网页授权是怎么一回事呢,一起来看看吧!
官方的文档有很详细的说明,这里就主要分析重要的几点:
第一,网页授权分类及说明:
1、以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)
2、以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。
第二,网页授权流程分为四步:
1、引导用户进入授权页面同意授权,获取code
2、通过code换取网页授权access_token(与基础支持中的access_token不同)
3、如果需要,开发者可以刷新网页授权access_token,避免过期
4、通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)
了解到网页授权的这些知识,下面就开始去实现吧~
开始的准备工作:在接口配置中,设置对应的回调域名,地址:“开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项
由于我的是个人,因此使用测试账号的配置:
配置好了,就可以进行开发了,首先来看一下具体的流程:
其实很多功能点,前面已经实现过,只用改一下调用地址和参数即可。
首先,调用的定义链接:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
链接中需要使用urlEncode对链接进行处理,可以在工具类中添加一个转码方法:
1 /** 2 * 对URL地址进行EnCode处理 3 * @param url 4 * @return 5 */ 6 public static String urlEnCode(String url) 7 { 8 String enCodedUrl = ""; 9 10 try 11 { 12 enCodedUrl = URLEncoder.encode(url, "utf-8"); 13 } 14 catch (UnsupportedEncodingException e) 15 { 16 // TODO Auto-generated catch block 17 e.printStackTrace(); 18 System.out.println("转码失败!"); 19 } 20 21 return enCodedUrl; 22 }
另外将其他参数补充完整,可以得到一个访问链接。
在这里,可以结合之前学的菜单的处理,可以定义一个菜单进行专门的授权验证,这里需要改造上一节学到的点,具体如下:
1 /** 2 * 定义菜单属性 3 * @return 4 */ 5 private Menu getMenu() 6 { 7 Menu menu = new Menu(); 8 9 // 建3个导航菜单 10 LevelMenu tLevelMenuOne = new LevelMenu(); 11 tLevelMenuOne.setName("Damon"); 12 LevelMenu tLevelMenuTwo = new LevelMenu(); 13 tLevelMenuTwo.setName("Panou"); 14 LevelMenu tLevelMenuThree = new LevelMenu(); 15 tLevelMenuThree.setName("Papaw"); 16 17 // 第一个导航菜单的子菜单 18 SubMenuButton tSubMenuButton_oneone = new SubMenuButton(); 19 tSubMenuButton_oneone.setType(SysCon.WECHAT_MENU_TYPE_VIEW); 20 tSubMenuButton_oneone.setName("网页授权"); 21 tSubMenuButton_oneone.setKey("11"); 22 tSubMenuButton_oneone.setUrl(getAuthorUrl()); 23 24 SubMenuButton tSubMenuButton_onetwo = new SubMenuButton(); 25 tSubMenuButton_onetwo.setType(SysCon.WECHAT_MENU_TYPE_CLICK); 26 tSubMenuButton_onetwo.setName("swimming"); 27 tSubMenuButton_onetwo.setKey("12"); 28 29 // 加入导航菜单 30 tLevelMenuOne.setSub_button(new SubMenuButton[] 31 { tSubMenuButton_oneone, tSubMenuButton_onetwo }); 32 33 // 第二 个导航菜单的子菜单 34 SubMenuButton tSubMenuButton_twoone = new SubMenuButton(); 35 tSubMenuButton_twoone.setType(SysCon.WECHAT_MENU_TYPE_CLICK); 36 tSubMenuButton_twoone.setName("watching TV"); 37 tSubMenuButton_twoone.setKey("21"); 38 39 SubMenuButton tSubMenuButton_twotwo = new SubMenuButton(); 40 tSubMenuButton_twotwo.setType(SysCon.WECHAT_MENU_TYPE_CLICK); 41 tSubMenuButton_twotwo.setName("play games"); 42 tSubMenuButton_twotwo.setKey("22"); 43 44 SubMenuButton tSubMenuButton_twothree = new SubMenuButton(); 45 tSubMenuButton_twothree.setType(SysCon.WECHAT_MENU_TYPE_CLICK); 46 tSubMenuButton_twothree.setName("shopping"); 47 tSubMenuButton_twothree.setKey("23"); 48 49 // 加入导航菜单 50 tLevelMenuTwo.setSub_button(new SubMenuButton[] 51 { tSubMenuButton_twoone, tSubMenuButton_twotwo, tSubMenuButton_twothree }); 52 53 // 第三个导航菜单的子菜单 54 SubMenuButton tSubMenuButton_threeone = new SubMenuButton(); 55 tSubMenuButton_threeone.setType(SysCon.WECHAT_MENU_TYPE_CLICK); 56 tSubMenuButton_threeone.setName("cring"); 57 tSubMenuButton_threeone.setKey("31"); 58 59 SubMenuButton tSubMenuButton_threetwo = new SubMenuButton(); 60 tSubMenuButton_threetwo.setType(SysCon.WECHAT_MENU_TYPE_CLICK); 61 tSubMenuButton_threetwo.setName("laughing"); 62 tSubMenuButton_threetwo.setKey("32"); 63 64 // 加入导航菜单 65 tLevelMenuThree.setSub_button(new SubMenuButton[] 66 { tSubMenuButton_threeone, tSubMenuButton_threetwo }); 67 68 menu.setButton(new MenuButton[] 69 { tLevelMenuOne, tLevelMenuTwo, tLevelMenuThree }); 70 71 return menu; 72 73 } 74 75 /** 76 * 获取微信网页授权页面链接 77 * @return 78 */ 79 private String getAuthorUrl() 80 { 81 String uri = "http://damonhouse.iok.la/Servlet/WeChatAuthorService"; 82 83 uri = WeChatUtil.urlEnCode(uri); 84 85 String authorUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect"; 86 87 authorUrl = authorUrl.replace("APPID", "appid").replace("REDIRECT_URI", uri).replace("SCOPE", "snsapi_userinfo"); 88 89 return authorUrl; 90 }
先看一下具体效果:
点击会弹出一个授权的页面(由于我已经授权过,所以这里会自动登录)
这个时候后台还没有对应的处理,我们需要进行返回页面的处理,这里定义对应的service类,在doGet方法中进行处理:
1 /** 2 * 微信授权接口类 3 * @author Damon 4 */ 5 public class WeChatAuthorService extends HttpServlet 6 { 7 8 @Override 9 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException 10 { 11 // TODO Auto-generated method stub 12 System.out.println("授权成功,进行返回处理!"); 13 req.setCharacterEncoding("utf-8"); 14 resp.setCharacterEncoding("utf-8"); 15 16 String code = req.getParameter("code"); 17 18 String state = req.getParameter("state"); 19 20 System.out.println("code :" + code + " and stat :" + state); 21 22 // 业务处理,获取授权用户信息 23 WeChatAuthorBL tWeChatAuthorBL = new WeChatAuthorBL(); 24 AuthorUserInfo tAuthorUserInfo = tWeChatAuthorBL.getAuthorData(code, state); 25 26 req.setAttribute("nickname", tAuthorUserInfo.getNickname()); 27 // 获取信息成功,回写成功页面 28 req.getRequestDispatcher("../wechat/authorsucc.jsp").forward(req, resp); 29 30 } 31 32 @Override 33 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException 34 { 35 // TODO Auto-generated method stub 36 super.doPost(req, resp); 37 } 38 39 40 }
可以从授权中获取到 code 和state(可自定义用与此处校验), 然后就是通过code来进行获取授权的用户信息:
从前面分析可知,需要先获取授权的acces_token,然后才能获取到授权的用户信息,那么结合前面2节内容,先定义2个实体类:
1、用户授权Token类
1 /** 2 * 用户授权Token 3 * @author Damon 4 */ 5 public class AuthorToken 6 { 7 // 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同 8 private String access_token = ""; 9 10 // access_token接口调用凭证超时时间,单位(秒) 11 private int expires_in = 0; 12 13 // 用户刷新access_token =""; 14 private String refresh_token = ""; 15 16 // 用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID 17 private String openid = ""; 18 19 // 用户授权的作用域,使用逗号(,)分隔 20 private String scope = ""; 21 22 public String getAccess_token() 23 { 24 return access_token; 25 } 26 27 public void setAccess_token(String access_token) 28 { 29 this.access_token = access_token; 30 } 31 32 public int getExpires_in() 33 { 34 return expires_in; 35 } 36 37 public void setExpires_in(int expires_in) 38 { 39 this.expires_in = expires_in; 40 } 41 42 public String getRefresh_token() 43 { 44 return refresh_token; 45 } 46 47 public void setRefresh_token(String refresh_token) 48 { 49 this.refresh_token = refresh_token; 50 } 51 52 public String getOpenid() 53 { 54 return openid; 55 } 56 57 public void setOpenid(String openid) 58 { 59 this.openid = openid; 60 } 61 62 public String getScope() 63 { 64 return scope; 65 } 66 67 public void setScope(String scope) 68 { 69 this.scope = scope; 70 } 71 72 }
2、授权用户信息类
1 /** 2 * 通过网页授权获取的用户信息 3 * @author Damon 4 */ 5 public class AuthorUserInfo 6 { 7 // 用户的唯一标识 8 private String openid = ""; 9 10 // 用户昵称 11 private String nickname = ""; 12 13 // 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知 14 private String sex = ""; 15 16 // 用户个人资料填写的省份 17 private String province = ""; 18 19 // 普通用户个人资料填写的城市 20 private String city = ""; 21 22 // 国家,如中国为CN 23 private String country = ""; 24 25 // 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。 26 private String headimgurl = ""; 27 28 // 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom) 29 private List<String> privilege = new ArrayList<String>(); 30 31 // 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。 32 private String unionid = ""; 33 34 public String getOpenid() 35 { 36 return openid; 37 } 38 39 public void setOpenid(String openid) 40 { 41 this.openid = openid; 42 } 43 44 public String getNickname() 45 { 46 return nickname; 47 } 48 49 public void setNickname(String nickname) 50 { 51 this.nickname = nickname; 52 } 53 54 public String getSex() 55 { 56 return sex; 57 } 58 59 public void setSex(String sex) 60 { 61 this.sex = sex; 62 } 63 64 public String getProvince() 65 { 66 return province; 67 } 68 69 public void setProvince(String province) 70 { 71 this.province = province; 72 } 73 74 public String getCity() 75 { 76 return city; 77 } 78 79 public void setCity(String city) 80 { 81 this.city = city; 82 } 83 84 public String getCountry() 85 { 86 return country; 87 } 88 89 public void setCountry(String country) 90 { 91 this.country = country; 92 } 93 94 public String getHeadimgurl() 95 { 96 return headimgurl; 97 } 98 99 public void setHeadimgurl(String headimgurl) 100 { 101 this.headimgurl = headimgurl; 102 } 103 104 public List<String> getPrivilege() 105 { 106 return privilege; 107 } 108 109 public void setPrivilege(List<String> privilege) 110 { 111 this.privilege = privilege; 112 } 113 114 public String getUnionid() 115 { 116 return unionid; 117 } 118 119 public void setUnionid(String unionid) 120 { 121 this.unionid = unionid; 122 } 123 124 }
下一步,就是通过调用接口来实现我们的功能了~
1 /** 2 * 获取授权用户 3 * @param code 4 * @param state 5 * @return 6 */ 7 public AuthorUserInfo getAuthorData(String code, String state) 8 { 9 10 // 1、通过code获取授权的authortoken 11 AuthorToken tAuthorToken = getAuthorToken("appid", "appsecret", code); 12 13 // 2、通过获取的 access_token和 openid 获取用户信息 14 AuthorUserInfo tAuthorUserInfo = getAuthorUserInfo(tAuthorToken.getAccess_token(), tAuthorToken.getOpenid()); 15 16 return tAuthorUserInfo; 17 } 18 19 20 /** 21 * 获取授权的access_token 22 * @param appid 23 * @param appsceret 24 * @param code 25 * @return 26 */ 27 private AuthorToken getAuthorToken(String appid, String appsceret, String code) 28 { 29 30 String path = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code "; 31 32 path = path.replace("APPID", appid).replace("SECRET", appsceret).replace("CODE", code); 33 34 AuthorToken tAuthorToken = new AuthorToken(); 35 36 try 37 { 38 String strResp = WeChatUtil.doHttpsGet(path, ""); 39 40 System.out.println(strResp); 41 42 // 解析获取的token信息 43 Map<String, Object> tMap = WeChatUtil.jsonToMap(strResp); 44 45 System.out.println(tMap.toString()); 46 47 // 封装 authortoken 48 49 tAuthorToken.setAccess_token((String) tMap.get("access_token")); 50 tAuthorToken.setExpires_in(Integer.parseInt((String) tMap.get("expires_in"))); 51 tAuthorToken.setOpenid((String) tMap.get("openid")); 52 tAuthorToken.setScope((String) tMap.get("scope")); 53 tAuthorToken.setRefresh_token((String) tMap.get("refresh_token")); 54 55 } 56 catch (HttpException e) 57 { 58 // TODO Auto-generated catch block 59 e.printStackTrace(); 60 } 61 catch (IOException e) 62 { 63 // TODO Auto-generated catch block 64 e.printStackTrace(); 65 } 66 67 return tAuthorToken; 68 } 69 70 71 /** 72 * 通过授权的access_token及用户的openid来拉取用户信息 73 * @param access_token 74 * @param openid 75 * @return 76 */ 77 private AuthorUserInfo getAuthorUserInfo(String access_token, String openid) 78 { 79 String path = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN"; 80 81 path = path.replace("ACCESS_TOKEN", access_token).replace("OPENID", openid); 82 83 AuthorUserInfo tAuthorUserInfo = new AuthorUserInfo(); 84 85 try 86 { 87 String strResp = WeChatUtil.doHttpsGet(path, ""); 88 89 System.out.println(strResp); 90 91 // 解析获取的token信息 92 Map<String, Object> tMap = WeChatUtil.jsonToMap(strResp); 93 94 System.out.println(tMap.toString()); 95 96 // 封装 authortoken 97 tAuthorUserInfo.setOpenid((String) tMap.get("openid")); 98 tAuthorUserInfo.setNickname((String) tMap.get("nickname")); 99 tAuthorUserInfo.setSex((String) tMap.get("sex")); 100 tAuthorUserInfo.setCountry((String) tMap.get("country")); 101 tAuthorUserInfo.setProvince((String) tMap.get("province")); 102 tAuthorUserInfo.setCity((String) tMap.get("city")); 103 tAuthorUserInfo.setHeadimgurl((String) tMap.get("headimgurl")); 104 tAuthorUserInfo.setPrivilege((List<String>) tMap.get("privilege")); 105 tAuthorUserInfo.setUnionid((String) tMap.get("unionid")); 106 } 107 catch (HttpException e) 108 { 109 // TODO Auto-generated catch block 110 e.printStackTrace(); 111 } 112 catch (IOException e) 113 { 114 // TODO Auto-generated catch block 115 e.printStackTrace(); 116 } 117 118 return tAuthorUserInfo; 119 }
这些搞定,我们就已经获取到了用户的信息了。 最后我们来给用户一个好的界面体验~
这里新增一个页面,显示获取到的用户昵称:
1 <%@ page language="java" contentType="text/html; charset=GBK" 2 pageEncoding="GBK"%> 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 4 <html> 5 <head> 6 <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> 7 <title>damon‘s house</title> 8 </head> 9 <body> 10 <% 11 String nickname=request.getAttribute("nickname").toString(); 12 %> 13 <table><tr><%=nickname %> 您好,感谢授权Damon的奇趣小屋!</tr></table> 14 </body> 15 </html>
来看一下效果吧~
微信网页授权,获取用户信息就到这啦~