• Uniapp + SpringBoot 开发微信H5项目 微信公众号授权登录 JAVA后台(一、配置使用微信公众平台测试公众号)


    申请测试号进行调试开发,测试号拥有大部分服务号有的接口权限。

    一、接口配置信息填写校验

    这里需要填写一个URL和一个Token验证字符串
    我这里是用了natapp内网穿透 将本地的后台8080端口服务映射到了 http://x7zws8.natappfree.cc
    https://natapp.cn/在natapp官网注册账号并且申请免费隧道
    在这里插入图片描述
    申请完了之后把域名绑定到自己的后台
    在这里插入图片描述
    后台接口:

        @RequestMapping(value = "/check", produces = "text/plain;charset=UTF-8", method = {RequestMethod.GET, RequestMethod.POST})
        //微信服务器根据配置的token,结合时间戳timestamp和随机数nonce通过SHA1生成签名,发起get请求,检验token的正确性,
        //检验正确原样返回随机字符串echostr,失败返回空字符串
        public String check(HttpServletRequest request, HttpServletResponse response,
                            @RequestParam("signature") String signature,
                            @RequestParam("timestamp") String timestamp,
                            @RequestParam("nonce") String nonce,
                            String echostr) throws Exception {
            //若是为get请求,则为开发者模式验证
            if ("get".equals(request.getMethod().toLowerCase())) {
                String checkSignature = SHA1.creatSHA1("token", timestamp, nonce);
                if (checkSignature.equals(signature)) {
                    return echostr;
                }
            }
            return null;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    SHA1:

    import java.security.MessageDigest;
    import java.util.Arrays;
    
    //SHA1加密算法类
    public class SHA1 {
        /**
         *
         * @param token
         * @param timestamp 时间戳
         * @param nonce 随机字符串
         * @return 安全签名
         * @throws AesException
         */
        public static String creatSHA1(String token, String timestamp, String nonce) throws AesException
        {
            try {
                String[] array = new String[] { token, timestamp, nonce};
                StringBuffer sb = new StringBuffer();
                // 字符串排序
                Arrays.sort(array);
                for (int i = 0; i < 3; i++) {
                    sb.append(array[i]);
                }
                String str = sb.toString();
                // SHA1签名生成
                MessageDigest md = MessageDigest.getInstance("SHA-1");
                md.update(str.getBytes());
                byte[] digest = md.digest();
    
                StringBuffer hexstr = new StringBuffer();
                String shaHex = "";
                for (int i = 0; i < digest.length; i++) {
                    shaHex = Integer.toHexString(digest[i] & 0xFF);
                    if (shaHex.length() < 2) {
                        hexstr.append(0);
                    }
                    hexstr.append(shaHex);
                }
                return hexstr.toString();
            } catch (Exception e) {
                e.printStackTrace();
                throw new AesException(AesException.ComputeSignatureError);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    AesException:

    public class AesException extends Exception {
    
        public final static int OK = 0;
        public final static int ValidateSignatureError = -40001;
        public final static int ParseXmlError = -40002;
        public final static int ComputeSignatureError = -40003;
        public final static int IllegalAesKey = -40004;
        public final static int ValidateAppidError = -40005;
        public final static int EncryptAESError = -40006;
        public final static int DecryptAESError = -40007;
        public final static int IllegalBuffer = -40008;
    
        private int code;
    
        private static String getMessage(int code) {
            switch (code) {
                case ValidateSignatureError:
                    return "签名验证错误";
                case ParseXmlError:
                    return "xml解析失败";
                case ComputeSignatureError:
                    return "sha加密生成签名失败";
                case IllegalAesKey:
                    return "SymmetricKey非法";
                case ValidateAppidError:
                    return "appid校验失败";
                case EncryptAESError:
                    return "aes加密失败";
                case DecryptAESError:
                    return "aes解密失败";
                case IllegalBuffer:
                    return "解密后得到的buffer非法";
                default:
                    return null; // cannot be
            }
        }
    
        public int getCode() {
            return code;
        }
    
        public AesException(int code) {
            super(getMessage(code));
            this.code = code;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    校验走完之后配置测试号中 网页服务下的网页授权获取基本用户
    在这里插入图片描述
    在这里插入图片描述

    这里填自己前端项目运行后的地址 我的后端项目端口是8080 uniapp前端运行完了之后的端口是8081所以我配置的是本地的ipv4 ip+端口号

    二、后端配置 + 拼装URL + 通过Code获取用户OpenID和基本信息

    在SpringBoot项目的pom中引入依赖

            <dependency>
                <groupId>com.github.binarywanggroupId>
                <artifactId>weixin-java-mpartifactId>
                <version>4.6.0version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    创建配置类

    import com.ruoyi.system.domain.WechatConfig;
    import com.ruoyi.system.service.IWechatConfigService;
    import me.chanjar.weixin.mp.api.WxMpService;
    import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
    import me.chanjar.weixin.mp.config.WxMpConfigStorage;
    import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class WxMpConfig {
    
        @Autowired
        private IWechatConfigService wechatConfigService;
    
        @Bean
        public WxMpConfigStorage wxMpConfigStorage() {
            WxMpDefaultConfigImpl configStorage = new WxMpDefaultConfigImpl();
            WechatConfig wechatConfig = wechatConfigService.selectWechatConfigById(1L);
            //我这里是做成了获取数据库中保存的AppID和AppSercret正常情况下写死就可以
            configStorage.setAppId(wechatConfig.getAppId());
            configStorage.setSecret(wechatConfig.getAppSecret());
            return configStorage;
        }
    
        @Bean
        public WxMpService wxMpService(WxMpConfigStorage configStorage) {
            WxMpService wxMpService = new WxMpServiceImpl();
            wxMpService.setWxMpConfigStorage(configStorage);
            return wxMpService;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    之后就可以在Controller中正常使用了
    /authorize 接口其实只做了一步操作 就是构建获取code的url 构建完成之后返回给前端 前端直接重定向
    buildAuthorizationUrl其中的三个参数
    第一个是回调地址
    第二个填snsapi_base 或者 snsapi_userinfo
    snsapi_base 是之能获取到 openid 而 snsapi_userinfo 是可以获取到用户基本信息
    snsapi_base是静默获取 而 snsapi_userinfo 需要用户点击授权

    @RestController
    @RequestMapping("/wx")
    public class WeChatController extends BaseController {
        @GetMapping("/authorize")
        public AjaxResult test() {
            WxOAuth2Service oAuth2Service = wxMpService.getOAuth2Service();
            String redirectUrl = oAuth2Service.buildAuthorizationUrl("http://192.168.1.100:8081", "snsapi_userinfo", null);
            return success(redirectUrl);
        }
    
        @GetMapping("/userInfo")
        public AjaxResult userInfo(@RequestParam("code") String code) throws WxErrorException {
            WxOAuth2Service oAuth2Service = wxMpService.getOAuth2Service();
            WxOAuth2AccessToken wxMpOAuth2AccessToken = oAuth2Service.getAccessToken(code);
            logger.info("【wxMpOAuth2AccessToken:】{}", wxMpOAuth2AccessToken);
            String openId = wxMpOAuth2AccessToken.getOpenId();
            logger.info("【openid:】{}", openId);
            WxOAuth2UserInfo userInfo = oAuth2Service.getUserInfo(wxMpOAuth2AccessToken, "zh_CN");
            logger.info("【用户信息:】{}", userInfo.toString());
            return success(userInfo);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    二、uniapp前端调用

    前端首先调用 /authorize 获取拼装好的地址然后重定向并获取code
    getUrlCode来判断地址中是否包含code如果有的话拿着code去获取用户基本信息和openid

    <template>
    	<view class="content">
    		<image class="logo" :src="loginUser.headImgUrl ? loginUser.headImgUrl : '/static/logo.png'"></image>
    		<view class="text-area">
    			<text class="title">{{loginUser.openid}}</text>
    		</view>
    		<button type="default" @click="login">登录</button>
    	</view>
    </template>
    
    <script>
    	export default {
    		data() {
    			return {
    				loginUser:{}
    			}
    		},
    		onLoad(query) {
    			const that = this;
    			let code = that.getUrlCode();
    			if (code) {
    				console.log("有code")
    				console.log(code)
    				uni.request({
    					url: "http://192.168.1.100:8080/wx/userInfo",
    					data: {
    						code: code
    					},
    					success(res) {
    						console.log("获取到用户信息")
    						console.log(res.data.data);
    						that.loginUser = res.data.data;
    					}
    				})
    			} else {
    				console.log("没有code")
    			}
    		},
    		methods: {
    			login() {
    				uni.request({
    					url: "http://192.168.1.100:8080/wx/authorize",
    					success(res) {
    						window.location.href = res.data.msg;
    					}
    				})
    			},
    			getUrlCode() {
    				return (
    					decodeURIComponent(
    						(new RegExp("[?|&]" + "code" + "=" + "([^&;]+?)(&|#|;|$)").exec(
    							location.href
    						) || [, ""])[1].replace(/\+/g, "%20")
    					) || null
    				);
    			},
    		}
    	}
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
  • 相关阅读:
    SpringMVC基础篇(四)
    《编译原理》复习第1章~第5章
    【故事证明和概率公理】
    Dubbo多协议支持
    no main manifest attribute, in ./XXX.jar,如何解决?
    基于微信小程序的游戏账号交易买卖平台设计与实现(源码+lw+部署文档+讲解等)
    网安学习-Windows权限提升1
    读书笔记:多Transformer的双向编码器表示法(Bert)-4
    公交实时位置查询APP
    MySQL系统表information_schema.INNODB_TRX详解及查看当前运行事务
  • 原文地址:https://blog.csdn.net/qq_43193513/article/details/136558126