1. 程式人生 > >微信測試號實現個人第三方PC端網站二維碼登入(程式碼實現篇)

微信測試號實現個人第三方PC端網站二維碼登入(程式碼實現篇)

我頁面使用了生成二維碼的js,是網上拿到的(太多轉載,具體作者是啥不知道(#^.^#))

好,正文來啦,我程式碼中是使用了springboot(SSM)+redis+shiro。。。

頁面程式碼:

<!DOCTYPE html>
<html lang="zh_CN" xmlns:th="http://www.thymeleaf.org"
				xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta charset="UTF-8"/>
    <title>微信登入二維碼</title>
    <script src='/js/jquery-2.0.3.min.js'></script>
    <script src="/js/qrcode.js"></script>
    <script type="text/javascript">
    /*<![CDATA[*/
    
    function checkLogin(){
    	var i = 0;
    	var sessionId = $("#sessionId").val();
    	if(sessionId!=""){
    		$.ajax({
                url: "/wechat/polling",
                type: "POST",
                data:{sessionId:sessionId},
                success: function (data) {
                	if(data.status=="200"){
                		window.clearInterval(c);
                		alert(data.message);
                		location.href = "/";
                	}else{
                		i++;
                		if(i>60){
                			window.clearInterval(c);
                			alert("二維碼已失效!請重新整理二維碼。");
                		}
                	}
                }
        	});
    	}
    }
    
    function wechatLogin(){
    	$.ajax({
            url: "/wechat/wxLoginPage",
            type: "POST",
            success: function (data) {
            	$("#sessionId").val(data.sessionId);
            	
            	var qrcode = new QRCode(document.getElementById("code"), {
                    width : 200,//設定寬高
                    height : 200
                });
                qrcode.makeCode(data.uri);
                c = setInterval(checkLogin,5000);//輪詢查詢
            }
    	});
    }
    /*]]>*/
    </script>
</head>
<body>
	<shiro:guest>
		<input type="button" value="微信登入" onclick="wechatLogin()" />
	</shiro:guest>
	<shiro:user>
		<shiro:principal property="username" />您已登入。
	</shiro:user>
	
	<input type="hidden" id="sessionId"/>
	<br/><br/><br/><br/>
	<div id="code"></div> 
</body>
</html>


後臺controller(@RequestMapping("/wechat"))程式碼:

//pc點選微信登入,生成登入二維碼
    @RequestMapping(value = "/wxLoginPage",method = RequestMethod.POST)
    @ResponseBody
    public Map<String,String> wxLoginPage(HttpServletRequest request) throws Exception {
    	String sessionId = request.getSession().getId();
    	logger.info("sessionId:"+sessionId);
        String uri = weChatAuthService.getAuthorizationUrl("pc",sessionId);//設定redirect_uri和state=sessionId以及測試號資訊,返回授權url
        logger.info(uri);
        Map<String,String> map = new HashMap<String,String>();
        map.put("sessionId", sessionId);
        map.put("uri", uri);//用來前端生成二維碼
        return map;
    }
    
    //掃描二維碼授權成功,取到code,回撥方法
    @RequestMapping(value = "/pcAuth")
    @ResponseBody
    public String pcCallback(String code,String state,HttpServletRequest request,HttpServletResponse response) throws Exception {
        String result = weChatAuthService.getAccessToken(code);//根據code獲取access_token和openId,不懂看微信文件
        JSONObject jsonObject = JSONObject.parseObject(result);
        
        //String refresh_token = jsonObject.getString("refresh_token");
        String access_token = jsonObject.getString("access_token");
        String openId = jsonObject.getString("openId");
        
        logger.info("------------授權成功----------------");
        JSONObject infoJson = weChatAuthService.getUserInfo(access_token,openId);//根據token和openId獲取微信使用者資訊,不懂看我上一篇文章開始分享的連結
        if(infoJson!=null){
        	String nickname = infoJson.getString("nickName");
            logger.info("-----nickname-----"+nickname);
            logger.info("-----sessionId-----"+state);
            infoJson.put("openId", openId);
            redisTemplate.opsForValue().set(state, infoJson, 10*60, TimeUnit.SECONDS);
            return "登入成功!";
        }
	    return "登入失敗!";
    }
//輪詢查詢key
    @RequestMapping(value="/polling",method=RequestMethod.POST)
    @ResponseBody
    public Map<String, Object> polling(String sessionId,HttpServletRequest request,HttpServletResponse response){
    	Map<String, Object> resultMap = new LinkedHashMap<String, Object>();
    	
    	if(redisTemplate.hasKey(sessionId)){
    		JSONObject infoJson = (JSONObject)redisTemplate.opsForValue().get(sessionId);
    		redisTemplate.opsForValue().getOperations().delete(sessionId);
    		String openId = (String)infoJson.get("openId");
    		//根據openId判斷我們網站是否存在該使用者,資料庫使用者表會儲存使用者
            User user = userService.selectUserByWechat(openId);
            if (user == null) {
            	String nickname = (String)infoJson.get("nickName");
            	String sex = (String)infoJson.get("sex");
            	User newuser = new User();
            	newuser.setSex(sex);
            	newuser.setWechat(openId);
            	newuser.setNickname(nickname);
            	int i = userService.insertUser(newuser);//新增使用者
            	if(i<1){
            		resultMap.put("status", 500);
        	        resultMap.put("message", "登入失敗:");
        	        return resultMap;
            	}
            }
            //登入操作
    	    try {
    	    	UsernamePasswordToken token = new UsernamePasswordToken(openId, openId);//這裡是用shiro登入,反正該openId已經微信掃碼驗證
    	        SecurityUtils.getSubject().login(token);
    	        resultMap.put("status", 200);
    	        resultMap.put("message", "登入成功");
    	        
    	        //更新使用者最後登入時間
    	        Subject  currentUser = SecurityUtils.getSubject(); 
    			User luser = (User) currentUser.getPrincipal();
    	        User user1 = new User();
    	        user1.setId(luser.getId());
    	        user1.setLastLogDate(new Date());
    	        userService.updateUserByIdSelective(user1);//更新使用者方法
    	        
    	        
    	    } catch (Exception e) {
    	        resultMap.put("message", "未知系統錯誤:" + e.getMessage());
    	    }
    	    return resultMap;
    	}else{//not has key
    		resultMap.put("status", 0);
    		return resultMap;
    	}
    }

serviceImpl程式碼

package com.kun.service.impl;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriComponentsBuilder;

import com.alibaba.fastjson.JSONObject;
import com.kun.service.WeChatAuthService;

@Service
public class WeChatAuthServiceImpl extends DefaultAuthServiceImpl implements WeChatAuthService {

	private final Logger logger = LoggerFactory.getLogger(this.getClass());

	//請求此地址即跳轉到二維碼登入介面
    private static final String AUTHORIZATION_URL =
            "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect";

    // 獲取使用者 openid 和access——toke 的 URL
    private static final String ACCESSTOKE_OPENID_URL =
            "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";

    private static final String REFRESH_TOKEN_URL =
            "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=%s&grant_type=refresh_token&refresh_token=%s";

    private static final String USER_INFO_URL =
            "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN";

    private static final String APP_ID="wx957421889a04804d";
    private static final String APP_SECRET="9155791266381e14c670ea7fa1abb90b";
    private static final String SCOPE = "snsapi_userinfo";//  snsapi_userinfo  snsapi_base

    private String pcCallbackUrl = "https://7dc6440a.ngrok.io//wechat/pcAuth"; //pc回撥域名
    
    private String mobileCallbackUrl = "https://7dc6440a.ngrok.io//wechat/mobileAuth"; //mobile回撥域名

    /**
     * 第一步,帶著引數
     * appid:公眾號的唯一標識
     * redirect_uri:授權後重定向的回撥連結地址
     * response_type:返回型別,填寫code
     * scope:應用授權作用域,snsapi_base / snsapi_userinfo
     * state:非必傳,重定向後會帶上state引數,開發者可以填寫a-zA-Z0-9的引數值,最多128位元組
     * wechat_redirect:無論直接開啟還是做頁面302重定向時候,必須帶此引數
     * */
    @Override
    public String getAuthorizationUrl(String type,String state) throws UnsupportedEncodingException {
    	String callbackUrl = "";
    	Object urlState = "";
    	if("pc".equals(type)){//移動端 pc端回撥方法不一樣 
    		callbackUrl = URLEncoder.encode(pcCallbackUrl,"utf-8");
    		urlState = state;
    	}else if("mobile".equals(type)){
    		callbackUrl = URLEncoder.encode(mobileCallbackUrl,"utf-8");
    		urlState = System.currentTimeMillis();
    	}
        String url = String.format(AUTHORIZATION_URL,APP_ID,callbackUrl,SCOPE,urlState);
        return url;
    }

    /**
     * 第二步
     * 傳appid  secret code grant_type=authorization_code
     * 獲得 access_token openId等
     * */
    @Override
    public String getAccessToken(String code) {
        String url = String.format(ACCESSTOKE_OPENID_URL,APP_ID,APP_SECRET,code);

        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
        URI uri = builder.build().encode().toUri();

        String resp = getRestTemplate().getForObject(uri, String.class);
        logger.error("getAccessToken resp = "+resp);
        if(resp.contains("openid")){
            JSONObject jsonObject = JSONObject.parseObject(resp);
            String access_token = jsonObject.getString("access_token");
            String openId = jsonObject.getString("openid");;

            JSONObject res = new JSONObject();
            res.put("access_token",access_token);
            res.put("openId",openId);
            res.put("refresh_token",jsonObject.getString("refresh_token"));

            return res.toJSONString();
        }else{
            logger.error("獲取使用者資訊錯誤,msg = "+resp);
        	return null;
        }
    }

    //微信介面中,token和openId是一起返回,故此方法不需實現
    @Override
    public String getOpenId(String accessToken) {
        return null;
    }

    @Override
    public JSONObject getUserInfo(String accessToken, String openId){
        String url = String.format(USER_INFO_URL, accessToken, openId);
        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
        URI uri = builder.build().encode().toUri();

        String resp = getRestTemplate().getForObject(uri, String.class);
        logger.error("getUserInfo resp = "+resp);
        if(resp.contains("errcode")){
        	logger.error("獲取使用者資訊錯誤,msg = "+resp);
        	return null;
        }else{
            JSONObject data =JSONObject.parseObject(resp);

            JSONObject result = new JSONObject();
            result.put("id",data.getString("unionid"));
            result.put("sex",data.getString("sex"));
            result.put("nickName",data.getString("nickname"));
            result.put("avatar",data.getString("headimgurl"));

            return result;
        }
    }

    //微信的token只有2小時的有效期,過時需要重新獲取,所以官方提供了
    //根據refresh_token 重新整理獲取token的方法,本專案僅僅是獲取使用者
    //資訊,並將資訊存入庫,所以兩個小時也已經足夠了
    @Override
    public String refreshToken(String refresh_token) {

        String url = String.format(REFRESH_TOKEN_URL,APP_ID,refresh_token);

        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
        URI uri = builder.build().encode().toUri();

        ResponseEntity<JSONObject> resp = getRestTemplate().getForEntity(uri,JSONObject.class);
        JSONObject jsonObject = resp.getBody();

        String access_token = jsonObject.getString("access_token");
        return access_token;
    }
}

上面實現類繼承的類DefaultAuthServiceImpl

package com.kun.service.impl;

import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;

import javax.xml.transform.Source;

import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import com.kun.service.AuthService;

public abstract class DefaultAuthServiceImpl implements AuthService{
    public static RestTemplate getRestTemplate() {// 手動新增
        SimpleClientHttpRequestFactory requestFactory=new SimpleClientHttpRequestFactory();
        requestFactory.setReadTimeout(120000);
        List<HttpMessageConverter<?>> messageConverters = new LinkedList<>();
        messageConverters.add(new ByteArrayHttpMessageConverter());
        messageConverters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
        messageConverters.add(new ResourceHttpMessageConverter());
        messageConverters.add(new SourceHttpMessageConverter<Source>());
        messageConverters.add(new AllEncompassingFormHttpMessageConverter());
        messageConverters.add(new MappingJackson2HttpMessageConverter());
        RestTemplate restTemplate=new RestTemplate(messageConverters);
        restTemplate.setRequestFactory(requestFactory);
        return restTemplate;
    }
}


------------------
package com.kun.service;

import java.io.UnsupportedEncodingException;

import com.alibaba.fastjson.JSONObject;

public interface AuthService {
    public abstract String getAccessToken(String code);
    public abstract String getOpenId(String accessToken);
    public abstract String refreshToken(String code);
    public abstract String getAuthorizationUrl(String type,String state) throws UnsupportedEncodingException;
    public abstract JSONObject getUserInfo(String accessToken,String openId);
}

到這裡就可以實現微信二維碼登入我們的個人網站啦~~~~~