Java-微信 第三方登陆流程+详细代码

Java-微信 第三方登陆流程+详细代码流程图

J加粗样式ava后台流程图

微信三方登录工作流程
1.客户端发送授权码请求
2.当扫码成功之后,服务器向客户端返回对应的授权码(微信服务器调用回调接口)
3.通过授权码获取token(java程序发送的get请求)
4.通过token获取资源
官方给的API文档










参数说明
参数 是否必须 说明
appid 应用唯一标识
redirect_uri 请使用urlEncode对链接进行处理
response_type 填code
scope 应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login
state 用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验












注意:回调地址要改成本机的地址 C:\Windows\System32\drivers\etc
hosts文件 加上一段 127.0.0.1 填上回调域名

# For example: # localhost name resolution is handled within DNS itself. # 127.0.0.1 localhost # //填上回调域名  127.0.0.1 bugtracker.********.cn 

后台controller层代码 ,前端发送请求到 /wechat/tologin到这个接口

@Controller @RequestMapping("/wechat") public class WechatController { 
    @Autowired private IWechatService wechatService; //拉起二维码 @RequestMapping("/tologin") public String toLogin() { 
    String codeURl = WechatConstant.CODE_URL.replace("APPID", WechatConstant.APPID) .replace("REDIRECT_URI", WechatConstant.REDIRECT_URI); System.out.println("这是拉取二维码的接口"); //重定向跳转地址,二维码就出来了 return "redirect:" + codeURl; } 
//替换的回调地址, public static final String REDIRECT_URI="http://bugtracker..cn/wechat/callback"; 

用户扫描二维码点击确认成功后,会跳转到本机127.0.0.1/wechat/callback接口,并且携带授权码Code

 //二维码扫描成功后,跳转到回调接口,并且携带授权码 @RequestMapping("/callback") public String callback(String code) { 
    System.out.println(code + "后台获取到的COde"); return "redirect:http://localhost:8080/callback.html?code=" + code; //可以跳指定网址 //return "redirect:http://www.baidu.com"; } 

前端创建callback.html页面,页面用到了一个方法,获取?后面的数据 相当于获取Code授权码

 * js动态获取?后面的参数,并且封装成一个json对象 * @returns { 
   Object} */ function getParam(){ 
    var url=location.search; var param = new Object(); if(url.indexOf("?")!=-1){ 
    var str = url.substr(1) strs = str.split("&"); for(var i=0;i<strs.length;i++){ 
    param[strs[i].split("=")[0]]=unescape(strs[i].split("=")[1]); } } return param; } 

let param = getParam(); 就已经拿到授权码Code 发送请求到/wechat/handleCallback接口

<script type="text/javascript"> new Vue({ 
    el:"#app", mounted(){ 
    //获取当前页面?后面的参数,最终封装成一个json对象 let param = getParam(); //处理回调接口 this.$http.post("/wechat/handleCallback", param).then(res => { 
    let { 
   success, message, resultObj} = res.data; //如果success为true,并且openid有值,就证明它要跳转到绑定界面,用户和微信进行绑定 if(success&&resultObj.openid){ 
    location.href = "/binder.html?openid="+resultObj.openid }else if(success&& resultObj.token){ 
   //代表已经登录了 //把登录用户保存到浏览器里面 localStorage.setItem("token", resultObj.token); localStorage.setItem("loginUser", JSON.stringify(resultObj.loginUser)); location.href = "/index.html"; } }); } }) </script> 

后台wechat/handleCallback接口

 //处理回调接口 @PostMapping("/handleCallback") @ResponseBody //这里需要返回json数据 public AjaxResult handleCallback(@RequestBody Map<String, String> param) { 
    try { 
    //处理回调接口,拿到授权码Code 进行业务逻辑 Map<String, Object> map = wechatService.handleCallback(param.get("code")); //先不看下面的代码 Object openid = map.get("openid"); System.out.println(openid+"返回值openid,看看是否有值"); return AjaxResult.me().setResultObj(map); } catch (Exception e) { 
    e.printStackTrace(); return new AjaxResult(e.getMessage()); } } 
 //token对应的url地址 "https://api.weixin..com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code"; 
public class HttpClientUtils { 
    / * 发送get请求 * @param url 请求地址 * @return 返回内容 json */ public static String httpGet(String url){ 
    // 1 创建发起请求客户端 try { 
    HttpClient client = new HttpClient(); // 2 创建要发起请求-tet GetMethod getMethod = new GetMethod(url); // getMethod.addRequestHeader("Content-Type", // "application/x-www-form-urlencoded;charset=UTF-8"); getMethod.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET,"utf8"); // 3 通过客户端传入请求就可以发起请求,获取响应对象 client.executeMethod(getMethod); // 4 提取响应json字符串返回 String result = new String(getMethod.getResponseBodyAsString().getBytes("utf8")); return result; } catch (IOException e) { 
    e.printStackTrace(); } return null; } } 
  
     
    <dependency> <groupId>com.alibaba 
     groupId> <artifactId>fastjson 
      artifactId> <version>1.2.58 
       version>  
        dependency> 

代码看着多,一步一步来就好了,通过授权码 才能拿到令牌和openid(微信用户唯一标志)

 @Override public Map<String,Object> handleCallback(String code) { 
    //获取token的url地址 String tokenUrl = WechatConstant.TOKEN_URL.replace("APPID", WechatConstant.APPID) .replace("SECRET", WechatConstant.SECRET) .replace("CODE", code); //通过授权码Code获取token令牌,json字符串需要转成对象才能取值 String tokenJsonStr = HttpClientUtils.httpGet(tokenUrl); //把json字符串转为json对象 JSONObject jsonObject = JSONObject.parseObject(tokenJsonStr); //获取token String access_token = jsonObject.getString("access_token"); //微信用户唯一标志 String openid = jsonObject.getString("openid"); 
 //通过token获取微信用户资源 String userinfo_url = WechatConstant.USERINFO_URL.replace("ACCESS_TOKEN", access_token).replace("OPENID", openid); //发送get请求 String userinfoJsonstr = HttpClientUtils.httpGet(userinfo_url); //把用户信息数据转为json对象 jsonObject = JSONObject.parseObject(userinfoJsonstr); //通过openid查询数据库中是否有对应的数据(注意:联表查询, t_wxuser和t_logininfo表) Wechat wechat = wechatMapper.loadByOpenid(openid); //创建一个map集合,封装数据,然后返回给前端,方便绑定微信用户 Map<String, Object> map = new HashMap<>(); //如果Wechat为空,就证明是第一次扫码,第一次扫码就要先添加微信用户 if (wechat == null) { 
    wechat = new Wechat(); //设置属性值 唯一标志 wechat.setOpenid(openid); //设置昵称 wechat.setNickname(jsonObject.getString("nickname")); //设置性别  wechat.setSex(jsonObject.getInteger("sex")); //设置地址 国家、省份、城市 wechat.setAddress(jsonObject.getString("country")+" " + jsonObject.getString("province")+" " +jsonObject.getString("city")); //设置头像 wechat.setHeadimgurl(jsonObject.getString("headimgurl")); //保存微信用户基本信息 wechatMapper.save(wechat); //返回某个状态,然后前端根据这个状态跳转到绑定界面 map.put("openid", openid); return map; }else{ 
   //如果wechat不为空,就证明不是第一次扫码 LoginInfo loginInfo = wechat.getLoginInfo(); //如果loginInfo为空,依然跳转到绑定界面 if (loginInfo == null) { 
    //返回某个状态,然后前端根据这个状态跳转到绑定界面 map.put("openid", openid); return map; } //使用redisTemplate先要引入 redis jar包 下面附上了 //loginInfo不为空,就证明是已经绑定过了,直接登录即可 String token = UUID.randomUUID().toString(); redisTemplate.opsForValue().set(token,loginInfo,30, TimeUnit.MINUTES); map.put("token", token); map.put("loginUser", loginInfo); return map; } } 
  
    <dependency> <groupId>org.springframework.boot 
     groupId> <artifactId>spring-boot-starter-data-redis 
      artifactId>  
       dependency> 

callback.html

//处理回调接口 this.$http.post("/wechat/handleCallback", param).then(res => { 
    let { 
   success, message, resultObj} = res.data; //如果success为true,并且openid有值,就证明它要跳转到绑定界面,用户和微信进行绑定 if(success&&resultObj.openid){ 
    location.href = "/binder.html?openid="+resultObj.openid }else if(success&& resultObj.token){ 
   //代表已经登录了 //把登录用户保存到浏览器里面 localStorage.setItem("token", resultObj.token); localStorage.setItem("loginUser", JSON.stringify(resultObj.loginUser)); location.href = "/index.html"; } }); } 

在这里插入图片描述发送验证码上一篇文章已经讲过了
binder.html 跳转到这个页面是携带有openid 微信唯一标志的

 //准备参数 let param = { 
   "phone": this.user.phone}; //发送短信请求 this.$http.post("/code/sendBinderCode", param).then((res) => { 
    let { 
   success, message} = res.data; //提示用户一分钟以内不能连续发送多次 if(!success){ 
    this.errorMsg = message; } }); 
 //微信绑定手机发送验证码 @PostMapping("/sendBinderCode") public AjaxResult sendBinderCode(@RequestBody User user) { 
    try { 
    verificationService.sendBinderCode(user.getPhone()); return AjaxResult.me(); } catch (DiyException e) { 
    e.printStackTrace(); return new AjaxResult(e.getMessage()); } 
@Override public void sendBinderCode(String phone) throws DiyException { 
    //调用方法发送绑定验证码 sendCode(phone, VerificationConstant.USER_BINDER); } //发送验证码,不同常量不同短信 public void sendCode(String phone,String constant) throws DiyException { 
    //随机生成4位字符 String value = StrUtils.getComplexRandomString(4); //1分钟以内只能发送1次验证码 //先通过key 取值 key 就是手机号 加绑定标识常量 String valueCode = (String) redisTemplate.opsForValue().get(phone + ":" + constant); //判断存在redis中的验证码是否为空 //如果不为空且有效期在一分钟之内 System.out.println(valueCode + "测试验证码加时间戳"); if (!StringUtils.isEmpty(valueCode)) { 
    //获取验证码第一次创建的时间,截取时间戳 String beginTime = valueCode.split(":")[1]; //现在的时间 减去第一次创建的时候 如果小于1分钟之内 if ((System.currentTimeMillis()) - Long.valueOf(beginTime) <= 60 * 1000) { 
    throw new DiyException("一分内不能发送多次验证码"); }//超过1分钟且小于5分钟 //还是发送第一次的验证码过去 value = valueCode.split(":")[0]; }//如果通过key获取到的验证码为空,证明已经超过5分钟 redisTemplate.opsForValue().set(phone + ":" + constant, value + ":" + System.currentTimeMillis(), 5, TimeUnit.MINUTES); //发送验证码 String str = "尊敬的用户,你的验证码为:" + value + ",请在5分钟之内使用"; //调用接口发送短信,这是真的发到手机里,需要发送短信就解开注释 , //SendMsgUtils.send(phone, str); System.out.println("发送成功" + value); } 

前台拿到验证码登陆 手机号和验证码全正确并且没有过期的情况,点击注册后发送请求到/wechat/binder 并且携带user对象,user对象有 phone 手机号 code验证码 type账号类型 默认是1,还有openid

 //给注册按钮注册事件 register(){ 
    let param = getParam(); //添加openid属性 this.user.openid = param.openid; //校验 this.$http.post("/wechat/binder", this.user).then(res => { 
    let { 
   success, message,resultObj} = res.data; if(!success){ 
    //这是后台抛出的异常信息 this.errorMsg = message; }else{ 
    localStorage.setItem("token",resultObj.token); //JSON.stringify是json对象转换成字符串 localStorage.setItem("loginUser",JSON.stringify(resultObj.loginUser)); location.href = "/index.html"; } }); } 

后台 使用loginInfoDto临时对象接收数据,

@Data public class LoginInfoDto { 
    //手机号 private String phone; //验证码 private String code; //用户名 private String username; //密码 private String password; //重得密码 private String configpassword; //类型 1为门户用户 0为后台管理 private Integer type; //微信用户唯一标志 private String openid; } 
 //绑定 @PostMapping("/binder") @ResponseBody public AjaxResult binder(@RequestBody LoginInfoDto loginInfoDto) { 
    System.out.println(loginInfoDto+"绑定的微信手机号信息"); try { 
    Map<String, Object> map = wechatService.binder(loginInfoDto); return AjaxResult.me().setResultObj(map); } catch (Exception e) { 
    e.printStackTrace(); return new AjaxResult(e.getMessage()); } } 
@Override public Map<String,Object> binder(LoginInfoDto loginInfoDto) throws CustomException { 
    //校验数据 checkData(loginInfoDto); //通过手机号码和type在t_logininfo中查询是否有数据 LoginInfo loginInfo = logininfoMapper.loadByUsernameAndType(loginInfoDto.getPhone(), loginInfoDto.getType()); //如果为空,就要新建账号 if (loginInfo == null) { 
    //创建LoginInfo对象,创建后返回主键 loginInfo = createLoginInfo(loginInfoDto); //保存logininfo对象 logininfoMapper.save(loginInfo); //创建User对象 User user = createUser(loginInfo); //保存user对象 userMapper.save(user); } //绑定微信 wechatMapper.binder(loginInfo.getId(), loginInfoDto.getOpenid()); //查询出来的数据为空--》添加logininfo 添加user 绑定微信用户 直接登录 //查询出来的数据不为空---》直接绑定, 直接登录 //一下代码就是直接在登录 String token = UUID.randomUUID().toString(); redisTemplate.opsForValue().set(token,loginInfo,30, TimeUnit.MINUTES); Map<String, Object> map = new HashMap<>(); map.put("token", token); map.put("loginUser", loginInfo); return map; } 

校验数据,难理解的应该就是验证码是否过期,手机发送验证码 设置的有效期为5分钟,我们通过手机号加绑定常量 来取value值的时候 如果为空,就代表已经过期了

 private void checkData(LoginInfoDto loginInfoDto) throws CustomException { 
    //判断手机号是否为空 if(StringUtils.isEmpty(loginInfoDto.getPhone())){ 
    throw new CustomException("手机号码不为空!!"); } //判断验证码是否为空 if(StringUtils.isEmpty(loginInfoDto.getCode())){ 
    throw new CustomException("验证码不能为空!!"); } //判断验证码是否过期,验证码需要通过手机号加上绑定常量来取, String codeValue = (String) redisTemplate.opsForValue().get(loginInfoDto.getPhone() + ":" + VerificationConstant.USER_BINDER); if(StringUtils.isEmpty(codeValue)){ 
    throw new CustomException("验证码已过期!!"); } //判断验证码是否正确,通过手机号加常量取的验证码是带有时间戳的,需要分割 取索引0的字符串 if(!loginInfoDto.getCode().toUpperCase().equals(codeValue.split(":")[0].toUpperCase())){ 
    throw new CustomException("验证码错误!!"); } //判断账号类型是否为null,openid是否为空, if(loginInfoDto.getType()==null || StringUtils.isEmpty(loginInfoDto.getOpenid())){ 
    throw new CustomException("请完善基本信息!!"); } } 

创建loginInfo对象

 / * 创建LoginInfo对象 * @param loginInfoDto * @return */ private LoginInfo createLoginInfo(LoginInfoDto loginInfoDto) { 
    LoginInfo loginInfo = new LoginInfo(); //设置手机 loginInfo.setPhone(loginInfoDto.getPhone()); //设置类型 loginInfo.setType(loginInfoDto.getType()); //返回对象 return loginInfo; } 

创建User对象

 / * 创建用户对象 * @param loginInfo * @return */ private User createUser(LoginInfo loginInfo) { 
    User user = new User(); BeanUtils.copyProperties(loginInfo,user); user.setState(PethomeConstant.OK); user.setLoginInfo(loginInfo); return user; } 

附上WechatMapper.xml

 <mapper namespace="cn.baidu.user.mapper.WechatMapper"> <resultMap id="WechatResultMap" type="Wechat"> <id column="id" property="id"/> <result column="openid" property="openid"/> <result column="nickname" property="nickname"/> <result column="sex" property="sex"/> <result column="address" property="address"/> <result column="headimgurl" property="headimgurl"/> <association property="loginInfo" javaType="LoginInfo"> <id column="lid" property="id"/> <result column="lusername" property="username"/> <result column="lphone" property="phone"/> <result column="lemail" property="email"/>  
     association>  
      resultMap>  
      <select id="loadByOpenid" resultMap="WechatResultMap"> SELECT w.*,l.id lid,l.phone lphone,l.username lusername,l.email lemail FROM t_wxuser w LEFT JOIN t_logininfo l ON w.logininfo_id = l.id WHERE w.openid=#{openid}  
       select> <insert id="save" useGeneratedKeys="true" keyColumn="id" keyProperty="id"> INSERT INTO t_wxuser(openid, nickname, sex, address, headimgurl, logininfo_id) VALUES ( #{openid}, #{nickname}, #{sex}, #{address}, #{headimgurl}, #{loginInfo.id} )  
        insert> <update id="binder"> UPDATE t_wxuser SET logininfo_id=#{logininfoId} WHERE openid=#{openid}  
         update>  
          mapper> 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/216312.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午12:07
下一篇 2026年3月18日 下午12:07


相关推荐

  • 电脑蓝屏代码0x0000000f4_电脑蓝屏代码

    电脑蓝屏代码0x0000000f4_电脑蓝屏代码Stop:0x000000F40x000000f4CRITICAL_OBJECT_TERMINATION此问题主要由于系统重要进程被意外终止。请使用干净启动的操作来排除软件方面的干扰。开机按F8进安全模式,设置操作系统进入干净启动状态:a.点击开始菜单并在搜索框中输入msconfig,然后按回车键。b.点击“服务”标签卡,选择“隐藏所有的微软服务”,然后点击全部禁…

    2022年10月8日
    3
  • python 运行shell命令

    python 运行shell命令在 python 中实现运行多条 shell 命令今天小编就为大家分享一篇在 python 中实现运行多条 shell 命令 具有很好的参考价值 希望对大家有所帮助 一起跟随小编过来看看吧使用 py 时可能需要连续运行多条 shell 命令 coding UTF 8importsysre sys sys setdefaulten utf8 importsubpro os system cmd1 amp amp

    2026年3月19日
    3
  • MySQL呕血汇总–从基础到毕业【收藏篇】

    MYSQL目录前言一、MYSQL基础1.安装2.数据库介绍2.1.什么是数据库?2.2.实体(类)和表关系2.3.常见关系型数据库3.MYSQL基础操作❤重点❤3.1.启动关闭3.2.DDL(数据定义语言,这些语句定义不同的数据段、数据库、表、列、索引等数据库对象。常用语句关键字主要包括create,drop,alter等)3.2.1.操作数据库3.2.2.操作表3.3.DML(数据操作语句,用于添加、删除、更新和查询数据库记录,并检查数据完整性。常用语句关键字主要包括insert,delete,upda

    2022年4月9日
    31
  • 公司

    公司

    2026年3月13日
    3
  • Matlab GUI上位机界面实现串口通信

    Matlab GUI上位机界面实现串口通信MatlabGUI因项目需求,不得不学的又杂又浅,趁着还没彻底忘记,写下来一些关键注意点。命令行窗口输入guide→BlankGUI→确定根据自己的需求,拖动选择对应的工具,如下图所示双击每一个对象,就可以弹出其检查器,修改其属性,字体大小、粗细、位置等,其中最关键的是两个,一是String,二是Tag,String是用来修改对象中的文字,Tag是所调用的代码名,这个要好的…

    2022年5月15日
    52
  • DDL和DML的含义与区别「建议收藏」

    1、DDL和DML的含义1、DML(DataManipulationLanguage)数据操作语言-数据库的基本操作,SQL中处理数据等操作统称为数据操纵语言,简而言之就是实现了基本的“增删改查”操作。包括的关键字有:select、update、delete、insert、merge2、DDL(DataDefinitionLanguage)数据定义语言-用于定义和管理SQL数据库中的所有对象的语言,对数据库中的某些对象(例如,database,table)进行管理。包括的关键字有:crea

    2022年4月17日
    117

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号