知方号

知方号

Java实现微信登陆授权流程详解<微信授权登录网站怎么取消授权呢>

Java实现微信登陆授权流程详解

Java实现微信登陆授权流程详解

前期准备工作就不多说了,无非就是公众号平台账号、填写相关资耐心等待审核就好。

等通过之后,就可以看到测试用的 appId 和 appSecret 了,稍后我们要用到这两个 ID

接下来这里有一点要注意,在网址应用创建好之后的授权回调域填写顶级域名就好,微信文档里说的是,该域名下的所有页面都可以回调

微信开放平台第三方登录接口文档地址

代码实现微信授权

首先,我们要导入一些依赖,这里由于我当时找过好几个版本,依赖可能有些是多余的,都放在这里了QAQ

com.alibabaeasyexcel2.1.4com.alibabafastjson1.2.74commons-httpclientcommons-httpclient3.1org.bouncycastlebcprov-jdk161.46junitjunit4.12org.codehaus.xfirexfire-core1.2.6org.bouncycastlebcprov-jdk161.46

然后是是工具类:

解密手机号:GetUserInfoUtil

package com.ruoyi.common.utils;import org.bouncycastle.util.encoders.Base64;import org.bouncycastle.jce.provider.BouncyCastleProvider;import javax.crypto.BadPaddingException;import javax.crypto.Cipher;import javax.crypto.IllegalBlockSizeException;import javax.crypto.NoSuchPaddingException;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import java.io.UnsupportedEncodingException;import java.security.*;import java.security.spec.InvalidParameterSpecException;import java.util.Arrays;public class GetUserInfoUtil { public static String getUserInfo(String encryptedData, String sessionKey, String iv) { String result = ""; // 被加密的数据 byte[] dataByte = Base64.decode(encryptedData); // 加密秘钥 byte[] keyByte = Base64.decode(sessionKey); // 偏移量 byte[] ivByte = Base64.decode(iv); try { // 如果密钥不足16位,那么就补足. 这个if 中的内容很重要 int base = 16; if (keyByte.length % base != 0) { int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0); byte[] temp = new byte[groups * base]; Arrays.fill(temp, (byte) 0); System.arraycopy(keyByte, 0, temp, 0, keyByte.length); keyByte = temp; } // 初始化 Security.addProvider(new BouncyCastleProvider()); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC"); SecretKeySpec spec = new SecretKeySpec(keyByte, "AES"); AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES"); parameters.init(new IvParameterSpec(ivByte)); // 初始化 cipher.init(Cipher.DECRYPT_MODE, spec, parameters); byte[] resultByte = cipher.doFinal(dataByte); if (null != resultByte && resultByte.length > 0) { result = new String(resultByte, "UTF-8"); } } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidParameterSpecException e) { e.printStackTrace(); } catch (NoSuchProviderException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return result; }}

解析微信接口:

package com.ruoyi.project.system.applet;import org.apache.commons.httpclient.HttpStatus;import java.io.BufferedReader;import java.io.InputStreamReader;import java.net.HttpURLConnection;import java.net.URL;import java.net.URLEncoder;import java.util.HashMap;import java.util.Map.Entry;import java.util.Set;/** 1. HttpUtil工具类 */public class HttpUtil { public static String doGet(String urlPath, HashMap params) throws Exception { StringBuilder sb = new StringBuilder(urlPath); if (params != null && !params.isEmpty()) { // 说明有参数 sb.append("?"); Set set = params.entrySet(); for (Entry entry : set) { // 遍历map里面的参数 String key = entry.getKey(); String value = ""; if (null != entry.getValue()) { value = entry.getValue().toString(); // 转码 value = URLEncoder.encode(value, "UTF-8"); } sb.append(key).append("=").append(value).append("&"); } sb.deleteCharAt(sb.length() - 1); // 删除最后一个& } // System.out.println(sb.toString()); URL url = new URL(sb.toString()); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5000); // 5s超时 conn.setRequestMethod("GET"); if (conn.getResponseCode() == HttpStatus.SC_OK) {// HttpStatus.SC_OK == // 200 BufferedReader reader = new BufferedReader(new InputStreamReader( conn.getInputStream())); StringBuilder sbs = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sbs.append(line); } // JSONObject jsonObject = new JSONObject(sbs.toString()); return sbs.toString(); } return null; }}

接下来,就开始我们的表演了

首先,我们要通过前端给我们传的 code ,来调取接口获得自己需要的信息:

1、获取session_key

要获取用户的手机号,需要用到 session_key,那么这个 session_key 怎么获得呢,请看

@RestController@RequestMapping("/login")@Api(value = "微信登录模块")public class AppletLogin implements Serializable{ private static final Logger log = LoggerFactory.getLogger(AppletLogin.class); @Autowired private ILoggerUserService loggerUserService; /** * 登录 * @param code * @return */ @ApiOperation(value = "登录") @PostMapping("/appletLogin") public AjaxResult appletLogin(@Param("code")@RequestParam String code) { //校验请求参数 if(StringUtils.isEmpty(code)){ return AjaxResult.error("请求参数存在空值"); } String AppId = "前面获取的AppId"; String AppSecret = "前面获取的AppSecret"; //请求微信服务器,用code换取openid、sessionKey String result=""; try{ result = HttpUtil.doGet( "https://api.weixin.qq.com/sns/jscode2session?appid=" + AppId + "&secret=" + AppSecret + "&js_code=" + code + "&grant_type=authorization_code", null); } catch (Exception e) { e.printStackTrace(); } //解析从微信服务器上获取到的json字符串 JSONObject jsonObj = JSONObject.parseObject(result); if(jsonObj == null){ return AjaxResult.error("服务器请求微信接口获取参数失败"); } //封装返回的数据 HashMap returnHashMap = new HashMap(); String sessionKey = jsonObj.get("session_key").toString(); String openId = jsonObj.get("openid").toString(); //GetUserInfoUtil.getUserInfo() returnHashMap.put("sessionKey",sessionKey); returnHashMap.put("openId",openId); return AjaxResult.success("用户信息",returnHashMap); }}

这里的 AppId、AppSecret 都是一开始就知道的,我们直接放到里面,用前端传的 code 值,调用接口,返回 sessionKey、openId,这里的两个值我们下面都要用到。

2、解密手机号

解密手机号,我们要用到三个参数,sessionKey、encryptedData、iv,已知 sessionKey 我们有了,那么剩下两个呢?别急,这两个参数是前端给我们传的,我们直接拿过来用就可以了

/** * 根据opendid 添加用户手机号 * @param sessionKey * @param encryptedData * @param iv * @return */ @ApiOperation(value = "添加用户手机号") @GetMapping ("/phone") public AjaxResult phone(String sessionKey, String encryptedData, String iv){ if(StringUtils.isEmpty(sessionKey) || StringUtils.isEmpty(encryptedData) || StringUtils.isEmpty(iv)){ return AjaxResult.error("请求参数存在空值"); } // 微信小程序--手机号解密 String json = AES.wxDecrypt(encryptedData, sessionKey, iv); //json:{"phoneNumber":"17865181175","purePhoneNumber":"17865181175","countryCode":"86","watermark":{"timestamp":1587460231,"appid":"wxe493cddc6e0d931c"}} if(StringUtils.isEmpty(json)){ return AjaxResult.error("未获取到手机号"); } Map maps = (Map)JSON.parse(json); String phoneNumber = (String)maps.get("phoneNumber"); //返回用户信息 HashMap returnHashMap = new HashMap(); returnHashMap.put("phoneNumber",phoneNumber); return AjaxResult.success("用户信息",returnHashMap); }

注意,这里的 AES.wxDecrypt() 方法,所用到的工具类就是我们前面有提到的,解密手机号的工具类,当然了,也可以直接写在接口里,我只是封装了一下,不然代码看起来就比较乱了。

到了这一步,用户的手机号,我们就拿到了,大功告成!

3、那么用户信息呢?

用户的昵称、头像、地址等个人信息(不包括手机号),这些我们怎么拿到呢?这里就不得不说一下微信比较坑的地方了,以前没有改版的时候,通过上面的方法,我们可以直接获取到用户的个人信息和手机号,现在不行了,要么就按上面的步骤只能拿手机号,要么就按下面的步骤,拿用户信息,请看

@RestController@RequestMapping("/login")@Api(value = "微信登录模块")public class AppletLogin implements Serializable{ private static final Logger log = LoggerFactory.getLogger(AppletLogin.class); @Autowired private ILoggerUserService loggerUserService; /** * 登录 * @param code * @return */ @ApiOperation(value = "登录") @PostMapping("/appletLogin") public AjaxResult appletLogin(@Param("accessToken")@RequestParam String accessToken, @Param("openId")@RequestParam String openId) { //校验请求参数 if(StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(openId)){ return AjaxResult.error("请求参数存在空值"); } //请求微信服务器,用code换取openid String result=""; try{ result = HttpUtil.doGet( "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN", null); } catch (Exception e) { e.printStackTrace(); } //解析从微信服务器上获取到的json字符串 JSONObject userinfo= JSONObject.parseObject(result); if(userinfo== null){ return AjaxResult.error("服务器请求微信接口获取参数失败"); } /*{ "openid":" OPENID", "nickname": NICKNAME, "sex":"1", "province":"PROVINCE", "city":"CITY", "country":"COUNTRY", "headimgurl":"https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46", "privilege":[ "PRIVILEGE1" "PRIVILEGE2" ], "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"}*/ return AjaxResult.success("用户信息",userinfo); }}

这是返回的参数信息 这里有些小伙伴就要问了,openId 我知道,是通过 code 拿到的,那这个 accessToken 是怎么获取的呢? 坑来了!!!accessToken 需要 code 通过调用另一个接口获得,那么在已知,code 只能使用一次的情况下,我们怎么通过 code 又获取 sessionKey,又获取 accessToken 呢?

不要慌,我有完美的解决方案:那就让用户授权两次呗!

让前端通过第二次授权,去获取用户的头像、昵称,然后传给我们,我们在根据手机号、openId,对用户的信息的唯一性进行确认,并存入数据库中。

4、保存用户数据 /** * 获取用户信息 * @return */ @ApiOperation(value = "获取用户信息") @PostMapping("/getUserInfo") public AjaxResult getUserInfo(@ApiParam("用户手机号") @RequestParam String phoneNumber, @ApiParam("openId") @RequestParam String openId, @ApiParam("微信昵称")@RequestParam String nickname, @ApiParam("头像路径")@RequestParam String headimgurl, @ApiParam(value = "上级业务员id") @RequestParam(required = false)Integer id) { if(StringUtils.isEmpty(phoneNumber) || StringUtils.isEmpty(openId) || StringUtils.isEmpty(nickname) || StringUtils.isEmpty(headimgurl)){ return AjaxResult.error("请求参数存在空值"); } //去数据库判断是否存在该用户 LoggerUser loggerUser = loggerUserService.selectLoggerUserByOpenId(openId); //如果存在该用户 if(StringUtils.isNotNull(loggerUser)){ //一、用户是客户经理 CemManager manager = managerService.selectCemManagerByMobile(phoneNumber); if (StringUtils.isNotNull(manager)){//设置客户经理id loggerUser.setHold2(manager.getManagerId().toString()); } //二、用户有上级业务员,且开启 if (id!=null){ CemManager byId = managerService.selectCemManagerById(id.longValue()); if (StringUtils.isNotNull(byId)){//设置上级业务员id if (byId.getState()==0){ loggerUser.setHold1(byId.getManagerId().toString()); } //三、用户有上级业务员,且关闭 } } //四、用户用户有上级业务员,但已删除 //五、用户无上级业务员 if(StringUtils.isNotEmpty(loggerUser.getMobile())){ //将用户手机号覆盖 loggerUser.setMobile(phoneNumber); } //昵称 loggerUser.setWxName(nickname); //图像路径 loggerUser.setHold4(headimgurl); //保存用户信息 loggerUserService.updateLoggerUser(loggerUser); return AjaxResult.success(loggerUser); } //如果是新用户,就添加用户到数据库中 LoggerUser newLoggerUser = new LoggerUser(); //一、用户是客户经理 CemManager manager = managerService.selectCemManagerByMobile(phoneNumber); if (StringUtils.isNotNull(manager)){//设置客户经理id newLoggerUser.setHold2(manager.getManagerId().toString()); } //二、用户有上级业务员,且开启 if (id!=null){ CemManager byId = managerService.selectCemManagerById(id.longValue()); if (StringUtils.isNotNull(byId)){//设置上级业务员id if (byId.getState()==0){ newLoggerUser.setHold1(byId.getManagerId().toString()); } //三、用户有上级业务员,且关闭 } } //四、用户用户有上级业务员,但已删除 //五、用户无上级业务员 //openId newLoggerUser.setHold3(openId); //昵称 newLoggerUser.setWxName(nickname); //手机号 newLoggerUser.setMobile(phoneNumber); //图像路径 newLoggerUser.setHold4(headimgurl); //新增用户信息 loggerUserService.insertLoggerUserInfo(newLoggerUser); return AjaxResult.success(newLoggerUser); }

现在,我们就在用户登陆授权后,获取了一条完整的用户信息,包括手机号、昵称、头像等个人信息,当然了,可别忘记这是需要用户授权两次的,上面的步骤,就按一、二、四来就可以了,记得要跟前端小伙伴沟通好哦!

好事定律:每件事最后都会是好事,如果不是好事,说明还没到最后。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至lizi9903@foxmail.com举报,一经查实,本站将立刻删除。