微信測試號實現個人第三方PC端網站二維碼登入(程式碼實現篇)
阿新 • • 發佈:2019-01-07
我頁面使用了生成二維碼的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);
}
到這裡就可以實現微信二維碼登入我們的個人網站啦~~~~~