天天看点

微信授权登录公众号应用

微信授权登录网上一大堆,重复性很高

下面是自己总结的开发过程,已经简化

在微信公众号中>接口权限中获得相关的权限

微信授权登录公众号应用

在修改中 设置网页授权域名,也就是你的回调地址,写域名即可,不需要http

微信授权登录公众号应用

然后填写域名,并下载校验文件,此文件必须能被访问到,不能被项目权限拦截

微信授权登录公众号应用

至于appid和私钥翻翻就能找到,开发项目

首先是获取code,获取有很多方式,第一种,前端传递给后台code;第二种后台直接获取,第二种比较规范,此文使用第一种,前端获取后再去请求后端,道理是一样的

第一种 前端获取   发送带有appid和回调地址的链接(地址为你前端随便一个截取code的地址,看你项目而定)>通过code去ajax请求后台地址>返回具体信息

第二种 后台获取 前端发送微信登录请求>后台拼接地址并重定向,redirect_url为前端截取code地址>通过code去ajax请求后台地址>返回具体信息

授权大致流程:获取code>获取openid、access_token、refresh_token>通过refresh_token刷新access_token>根据刷新后的access_token、openid获取用户信息(openid等参数意义看文档)

开始开发,此文使用第一种方式

第一步 前端发送带有参数链接获取code

body>
<h1>微信登录</h1>
<button onclick="clickSub()">点击登录</button>
</body>
<script>
    function clickSub() {
        alert("开始授权");
        window.location.href=("https://open.weixin.qq.com/connect/oauth2/authorize?appid=你的appid&redirect_uri=获取code的前台地址&response_type=code&scope=snsapi_userinfo&state=STATE&connect_redirect=1#wechat_redirect");

    }
           

第二步 通过redirect_uri重定向的地址截取code,每次请求(第一步)都会携带不同的code参数,可用微信开发工具查看地址栏变化,code是一次性的

<body>
<h1>登录成功</h1>
</body>
<script>
    //截取code
    function GetRequest() {
        var url = location.search; //获取url中"?"符后的字符串
        var theRequest = new Object();
        if (url.indexOf("?") != -1) {
            var str = url.substr(1);
            strs = str.split("&");
            for(var i = 0; i < strs.length; i ++) {
                theRequest[strs[i].split("=")[0]]=(strs[i].split("=")[1]);
            }
        }
        return theRequest;
    }
    //截取到的code
    var Param= new Object();
    Param= GetRequest();
    var code = Param['code'];
    //json格式请求后台
    var datas = {"code":code};
    $.ajax({
        type: 'POST',
        url: '.../api/wxlogin/auth',
        contentType : 'application/json',
        dataType : 'json',
        data :JSON.stringify(datas),
        success: function(data){
            console.log("用户姓名:"+data.nickname)
        },
        error: function(result) {
            alert("获取出错了!")
        }
    });
</script>
           

第三步 通过code获取access_token等信息

service

public static String APPID = "你的appid";
    public static String APPSECRET = "你的APPSECRET";

    public ResultResponseVo auth(String codes)throws Exception{
        ResultResponseVo rrVo = new ResultResponseVo();

		//将用户信息保存到map种
        Map map = new HashMap();
        try {
            //第一步 通过code获取access_token
            String code=codes;
            if(null==code || "".equals(code)){
                rrVo.setResult(HttpResultStatus.FAIL);
                rrVo.setMsg("未获取code");
                return rrVo;
            }
	    // 根据code和appid和appsecret去获取openid和access_token、refresh_token
            String url="https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + APPID+
                    "&secret=" +APPSECRET+
                    "&code=" +code+
                    "&grant_type=authorization_code";
			//请求结果
            String result1 = HttpClientManager.getUrlData(url);
			//格式化
            Map<String,Object> data1 = JSONObject.fromObject(result1);
            String openid=data1.get("openid").toString();
            if(null==openid || "".equals(openid)){
                rrVo.setResult(HttpResultStatus.FAIL);
                rrVo.setMsg("openid不能为空");
                return rrVo;
            }
			
            String accessToken=data1.get("access_token").toString();
            String refreshToken=data1.get("refresh_token").toString();
            //第二步 刷新或续期access_token使用
            String refresh_token = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid="+APPID +
                    "&grant_type=refresh_token" +
                    "&refresh_token="+refreshToken+"";
			//请求结果
            String result2 = HttpClientManager.getUrlData(refresh_token);
			//格式化
            Map<String,Object> data2 = JSONObject.fromObject(result2);
            //最新openid
            String infoopenid = data2.get("openid").toString();
            //最新token
            String access_token = data2.get("access_token").toString();
            //第三步 获取用户个人信息(UnionID机制)
            String infoUrl="https://api.weixin.qq.com/sns/userinfo?access_token=" +accessToken+
                    "&openid=" +infoopenid+
                    "&;
			//请求结果
            String result3 = HttpClientManager.getUrlData(infoUrl);
            //格式化
			//openid 用户唯一编号 nickname用户姓名等等
            Map<String,Object> data3 = JSONObject.fromObject(result3);
            map.put("openid",data3.get("openid").toString());
            map.put("nickname",data3.get("nickname").toString());
            map.put("headimgurl",data3.get("headimgurl").toString());
            map.put("city",data3.get("city").toString());
            System.out.println("用户姓名-nickName:"+data3.get("nickname").toString());
			//下面可以开始你的项目逻辑,如保存到redis openid进来判断有无id,有就直接返回用户信息(数据库查询等等) 、保存到数据库用户信息等等
			
        } catch (Exception e) {
            e.printStackTrace();
            rrVo.setResult(HttpResultStatus.FAIL);
            rrVo.setMsg("服务器出错了");
            return rrVo;
        }
		//将信息封装返回
        rrVo.setData(map);
        rrVo.setResult(HttpResultStatus.SUCCESS);
        rrVo.setMsg(HttpResultStatus.SUCCESS.getMsg());
        return rrVo;
    }
           

HttpClientManager.java  网络请求类

public class HttpClientManager {

    /**
     * 执行url请求数据
     *
     * @param urlStr
     * @return
     * @throws Exception
     * @throws
     */
    public static String getUrlData(String urlStr) throws Exception {
        URL url = new URL(urlStr);
        URLConnection connection = url.openConnection();
        // 一旦发送成功,用以下方法就可以得到服务器的回应:
        String sCurrentLine = "";
        StringBuffer sTotalString = new StringBuffer();
        InputStream l_urlStream = connection.getInputStream();
        BufferedReader l_reader = new BufferedReader(new InputStreamReader(
                l_urlStream, "UTF-8"));
        while ((sCurrentLine = l_reader.readLine()) != null) {
            sTotalString.append(sCurrentLine);
        }
        return sTotalString.toString();
    }

    /**
     * 执行url请求数据
     *
     * @param urlStr
     * @return
     * @throws Exception
     * @throws
     */
    public static String postUrlData(String urlStr, String data)
            throws Exception {
        URL url = new URL(urlStr);
        HttpURLConnection http = (HttpURLConnection) url.openConnection();
        http.setRequestMethod("POST");
        http.setRequestProperty("Content-Type",
                "application/x-www-form-urlencoded");
        http.setDoOutput(true);
        http.setDoInput(true);
        System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒28
        System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒29
        // 30
        OutputStream os = http.getOutputStream();
        os.write(data.getBytes("UTF-8"));// 传入参数
        os.flush();
        os.close();
        // 一旦发送成功,用以下方法就可以得到服务器的回应:
        String sCurrentLine = "";
        String sTotalString = "";
        InputStream l_urlStream = http.getInputStream();
        BufferedReader l_reader = new BufferedReader(new InputStreamReader(
                l_urlStream, "UTF-8"));
        while ((sCurrentLine = l_reader.readLine()) != null) {
            sTotalString += sCurrentLine;
        }
        return sTotalString;
    }
}
           

ResultResponseVo.java 封装信息类

public class ResultResponseVo implements Serializable {

    private String code;//状态码
    private String msg;//处理消息
    private Object data = "";
    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    /**
     * 默认失败
     */
    public ResultResponseVo() {
        this.code = HttpResultStatus.FAIL.getCode();
        this.msg = HttpResultStatus.FAIL.getMsg();
    }

    /**
     * 指定默认Code
     *
     * @param enumItem
     */
    public ResultResponseVo(HttpResultStatus enumItem) {
        this.code = enumItem.getCode();
        this.msg = enumItem.getMsg();
    }

    /**
     * 设置调用结果
     *
     * @param enumItem
     */
    public void setResult(HttpResultStatus enumItem) {
        this.code = enumItem.getCode();
        this.msg = enumItem.getMsg();
    }

    /**
     * 设置调用结果
     */
    public void setResult(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    @Override
    public String toString() {
        return "ResultResponseVo{" +
                "code='" + code + '\'' +
                ", msg='" + msg + '\'' +
                ", data=" + data +
                '}';
    }
}
           

Controller

@Controller
@RequestMapping(value="/api/wxlogin")
public class ApiWeixinLoginController {
    private static Logger logger = LoggerFactory.getLogger(ApiWeixinLoginController.class);

    @Resource(name = "apiWeixinLoginService")
    private ApiWeixinLoginService apiWeixinLoginService;

    /**
     * 授权地址
     * @return
     * @throws Exception
     */
    @ResponseBody
    @RequestMapping(value = "/auth")
    public ResultResponseVo auth(@RequestBody String code) throws Exception{
        ResultResponseVo rrVo = apiWeixinLoginService.auth(code);
        return rrVo;
    }
           

到此处简单的就结束了,有几个坑点

1.微信在文档中一直强调的 access_token和普通版本不同 ,普通版是指获取录音权限、支付权限等等的access_token,那个是需要缓存,有次数限制,微信授权的access_token是没有次数限制的,只有频率限制,因此不需要缓存,并且是一次性,有时候你会弄混,去缓存微信授权的access_token,适得其反,会报错

2.code只能使用一次,因此,在获取用户信息后必须在重新定向到第一步,再走一遍授权流程

3.如果前后端分离,需要跨域,跨域代码网上一大堆

等等