WeChat third-party login

Reprinted from https://www.cnblogs.com/skillking/p/6735730.html

Available according to the blog process, but found that the blogger uploaded two categories less, now add

param class

public class Param {
	public static final CharSequence APPID = "wx90dc72b882ed01c6";
	public static final CharSequence SECRET = "c5d6967ebfe4dd574282a47326fab2a5";
	public static final String REDIRECT_URI = "http://bdf936fa.ngrok.io/wechat/loginByWeiXin";
}
OAuthInfo class
public class OAuthInfo {
	private String openid;
	private String access_token;
	public String getOpenid() {
		return openid;
	}
	public void setOpenid(String openid) {
		this.openid = openid;
	}
	public String getAccess_token() {
		return access_token;
	}
	public void setAccess_token(String access_token) {
		this.access_token = access_token;
	}
}

The following is the original text of the blogger

1. Knowledge before development

  1. The difference between WeChat open platform and WeChat public platform

    1.1 WeChat public platform:  

    ① Address: https://mp.weixin.qq.com/cgi-bin/loginpage?t=wxm2-login&lang=zh_CN

    ② The WeChat public platform is aimed at ordinary users, such as self-media and media, and the operators of the official WeChat public account of the company. Of course, your team or company has the strength to develop some content, and you can also call the interface in the public platform, such as Custom menu, automatic reply, query function.

    1.2 WeChat Open Platform:  

    ① Address: https://open.weixin.qq.com/

    WeChat Open Platform: For developers and third-party independent software developers. The open platform documentation seems to contain the interface in the WeChat Open Platform documentation.

  2、微信公众平台目前只支持80端口,且项目上传到服务器上面不容易debug啊?

    解决方法: ngrok 工具,可以根据本地项目映射成外网可以访问的地址、端口,默认映射为80端口。

      官网:       https://dashboard.ngrok.com

      存在问题: 每次重启,域名会变化,都需要项目中、微信公众平台配置,付费可以固定域名。。。

    使用方法: ① 注册后获得一个授权码,下载 ngrok

          ② 》CMD 进入 ngrok 目录

            》ngrok authtoken 授权码

            》ngrok http 8080

         ③看到这个页面,ngrok就为你分配了临时访问通道成功了^^

 

二、配置

  (每个微信号可以申请成为开发者测试账号进行微信功能开发的。)

  1、在公众平台中进行配置:

    地址: http://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index

    ①:配 置 “接口配置信息” (Token 在项目中我是配置为"handsomeKing",没错,我就是King ^^)

      url 改为自己映射的域名

    ② 配 置“功能服务” 的 “网页帐号”。注意,是域名

  好了,可以愉快的 coding 了!!!

三、可以开发啦

    1、上面的 “接口配置信息” 配置得 wechat/ownerCheck 方法是验证 这个url 为我自己的,handsomeKing 就是这个方法中的一个参数。微信这个小朋友会用Get方法带上4个参数给我服务器: signature(签名)、timestamp(时间戳)、nonce(随机数)、echostr(随机字符串),再加上咱们项目中自己配置的 token ,通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败:(talk is cheap, show me the code *&)

复制代码
 /**
     * 微信消息接收和token验证
     * @param model
     * @param request
     * @param response
     * @throws IOException
     */
    @RequestMapping("/ownerCheck")
    public void ownerCheck(Model model, HttpServletRequest request,HttpServletResponse response) throws IOException {
        System.out.println(111);
        boolean isGet = request.getMethod().toLowerCase().equals("get");
        PrintWriter print;
        if (isGet) {
            // 微信加密签名
            String signature = request.getParameter("signature");
            // 时间戳
            String timestamp = request.getParameter("timestamp");
            // 随机数
            String nonce = request.getParameter("nonce");
            // 随机字符串
            String echostr = request.getParameter("echostr");
            // 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
            if (signature != null && CheckoutUtil.checkSignature(signature, timestamp, nonce)) {
                try {
                    print = response.getWriter();
                    print.write(echostr);
                    print.flush();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
复制代码
复制代码
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class CheckoutUtil {
    // 与接口配置信息中的Token要一致
    private static String token = "handsomeKing";

    /**
     * 验证签名
     * 
     * @param signature
     * @param timestamp
     * @param nonce
     * @return
     */
    public static boolean checkSignature(String signature, String timestamp, String nonce) {
        String[] arr = new String[] { token, timestamp, nonce };
        // 将token、timestamp、nonce三个参数进行字典序排序
        // Arrays.sort(arr);
        sort(arr);
        StringBuilder content = new StringBuilder();
        for (int i = 0; i < arr.length; i++) {
            content.append(arr[i]);
        }
        MessageDigest md = null;
        String tmpStr = null;

        try {
            md = MessageDigest.getInstance("SHA-1");
            // 将三个参数字符串拼接成一个字符串进行sha1加密
            byte[] digest = md.digest(content.toString().getBytes());
            tmpStr = byteToStr(digest);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        content = null;
        // 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
        return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
    }

    /**
     * 将字节数组转换为十六进制字符串
     * 
     * @param byteArray
     * @return
     */
    private static String byteToStr(byte[] byteArray) {
        String strDigest = "";
        for (int i = 0; i < byteArray.length; i++) {
            strDigest += byteToHexStr(byteArray[i]);
        }
        return strDigest;
    }

    /**
     * 将字节转换为十六进制字符串
     * 
     * @param mByte
     * @return
     */
    private static String byteToHexStr(byte mByte) {
        char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
        char[] tempArr = new char[2];
        tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
        tempArr[1] = Digit[mByte & 0X0F];
        String s = new String(tempArr);
        return s;
    }
    public static void sort(String a[]) {
        for (int i = 0; i < a.length - 1; i++) {
            for (int j = i + 1; j < a.length; j++) {
                if (a[j].compareTo(a[i]) < 0) {
                    String temp = a[i];
                    a[i] = a[j];
                    a[j] = temp;
                }
            }
        }
    }
}
复制代码

2、用户用户同意授权,获取微信服务器传过来的俩参数: code、state。其中:

APPID: 微信开发者测试账号
REDIRECT_URI: 同意授权后跳转的 URL
复制代码
/**
     * 第一步:用户同意授权,获取code(引导关注者打开如下页面:)
     *  获取 code、state
     */
    public static String getStartURLToGetCode() {
        
        String takenUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect";
        takenUrl= takenUrl.replace("APPID", Param.APPID);
        takenUrl= takenUrl.replace("REDIRECT_URI", URL.encode(Param.REDIRECT_URI));
        //FIXME : snsapi_userinfo
        takenUrl= takenUrl.replace("SCOPE", "snsapi_userinfo");
        System.out.println(takenUrl);
        return takenUrl;
    }
复制代码

3、获取微信用户的 access_token、openid

APPID: 微信开发者测试账号
secret: 微信开发者测试账号密码
code:  上一步的 code
复制代码
/**
     * 获取access_token、openid
     * 第二步:通过code获取access_token
     * @param code url = "https://api.weixin.qq.com/sns/oauth2/access_token
     *                      ?appid=APPID
     *                      &secret=SECRET
     *                      &code=CODE
     *                      &grant_type=authorization_code"
     * */
    public static OAuthInfo getAccess_token(String code){
        
     
        String authUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code ";
        authUrl= authUrl.replace("APPID", Param.APPID);
        authUrl = authUrl.replace("SECRET", Param.SECRET);
        authUrl = authUrl.replace("CODE", code);
        String jsonString = HTTPRequestUtil.sendPost(authUrl,"");
        System.out.println("jsonString: " + jsonString);
        OAuthInfo auth = null;
        try {
            auth = (OAuthInfo) JacksonUtil.parseJSONToObject(OAuthInfo.class, jsonString);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return auth;
    }
    
复制代码

 

4、在数据库中查询 openid

  我是定义了一个对象 OauthInfo,包括 openid(用户的标识)属性。。。

  查询时库中存在就直接登录啦,不存在就先去登录页,将登录账号同 openid 绑定。

复制代码
/**
     * 微信引导页进入的方法
     * @return
     */
    @RequestMapping("/loginByWeiXin")
    public String loginByWeiXin(HttpServletRequest request, Map<String, Object> map) {
        // 微信接口自带 2 个参数
        String code = request.getParameter("code");
        String state = request.getParameter("state");
        System.out.println("code = " + code + ", state = " + state);
        
        if(code != null && !"".equals(code)) {
            // 授权成功, 微信获取用户openID
            OAuthInfo authInfo = WeiXinUtil.getAccess_token(code);
            String openid = authInfo.getOpenid();
            String access_token = authInfo.getAccess_token();
            
            if(access_token == null) {
                // Code 使用过 异常
                System.out.println("Code 使用过 异常.....");
                return "redirect:" + WeiXinUtil.getStartURLToGetCode();
            }
            
            // 数据库中查询微信号是否绑定平台账号
            SysUser sysUser = weiXinService.getUserByWeiXinID(openid);
            if(sysUser == null) {
                String randomStr = StringUtil.getRandomString(50);
                request.getSession().setAttribute(openid, randomStr);
                // 尚未绑定账号
                System.out.println("尚未绑定账号.....");
                return "redirect:/index.jsp?openid=" + openid + "&state=" + randomStr;
            }
            userController.doSomeLoginWorkToHomePage(sysUser.getMcid(), map);
            // 登录成功
            return "homePage";
        } 
        // 未授权
        return "redirect:" + WeiXinUtil.getStartURLToGetCode();
    
    }
复制代码

四、踩过的坑

  1、access_token 与 普通 access_token。这尼玛是啥关系?

   答:access_token: 登录授权会得到一个 access_token, 这个是用于标识登录用户(没有使用次数限制),绑定流程中用的都是 登录 access_token。

      普通 access_token:   调用其它微信应用接口时需要带上的 access_token,普通 access_token时效为 2H, 每个应用每天调用次数<= 2000次。(实际开发时必须加以处理。。我采用的是 quartz 调度^^)

  2、 uuid、openid。我该绑定uuid?不不不,openid?不不,到底绑哪个?

     答:uuid: 微信号绑定后才有的标识,对于任何应用该微信账号的 uuid 均相同。同一个微信账号不同的应用 uuid 相同。

       openid: 微信号对于每个应用均有一个不变的 openid,同一个微信账号不同的应用 openid 不同。 

五、参考的链接!感谢!!


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325976767&siteId=291194637