微信登陸Web應用解決方案
阿新 • • 發佈:2019-01-05
1.PC端
2.移動端
注意寫入事物回滾機制(因為涉及到操作多張表避免問題,)
接入微信登陸參考程式碼
1.微信開放平臺回撥函式
/**
* @param code 微信開放平臺重定向這裡,攜帶的code碼
* @param state 來自PC端還是Mobile端
*
* @author simon
* @date 2016/02/24
*/
@GET
@Path("wxlogin")
public void wxlogin(@QueryParam("code") String code,
@QueryParam ("state") String state,
@Context HttpServletRequest request,
@Context HttpServletResponse response) throws Exception {
if (!StringUtils.isEmpty(code)) {
//1.根據code請求access_token
Map<String, Object> map = CodeUtils.get_access_token(code);
String access_token = map.get("access_token" ).toString();
String openid = map.get("openid").toString();
//2.使用access_token去請求使用者資訊
Map<String, Object> userinfoMap = CodeUtils.get_userinfo(access_token, openid);
if (LuoboAppContext.currentUser() != null) {
WeichatBind weichatBind = new WeichatBind();
weichatBind.setUnionid(userinfoMap.get("unionid").toString());
weichatBind.setUserId(LuoboAppContext.currentUser().getId());
userService.createBind(weichatBind);//完成繫結
if(state.equals("01")){
response.sendRedirect("http://www.jkgst.com/main.html#!/user/base");//重定向到PC端頁面
}else{
response.sendRedirect("http://www.jkgst.com/m/#!/user/base");//重定向到移動端頁面
}
} else {
//當前為登陸操作,去繫結表查詢該微訊號是否已經繫結過
WeichatBind weichatBind = userService.getByUnionid(userinfoMap.get("unionid").toString());
if (weichatBind != null) {
//使用者已經繫結過
User user = userService.findById(weichatBind.getUserId());
//完成模擬登陸,重定向到主頁面即可
autoLogin(request,response,user.getUsername(),state);
} else {
//使用者第一次繫結,先去繫結手機號,先把userinfoMap放入session中
request.getSession().setAttribute("userinfoMap", userinfoMap);
//重定向到繫結手機號頁面
if(state.equals("01")){
//來自PC
response.sendRedirect("http://www.jkgst.com/main.html#!/bind");//去PC頁面完成繫結
}else{
//來自移動端
response.sendRedirect("http://www.jkgst.com/m/#!/bind");//去手機頁面完成繫結
}
}
}
}
}
2.後端自動登陸
/**
* 後端自動登陸
*
* @param type PC 或 Mobile
*
* @author simon
* @date 2016/02/26
* */
public void autoLogin(HttpServletRequest request,
HttpServletResponse response,String username,String type)
throws Exception {
ObjectWriter viewWriter = this.mapper.writerWithView(JsonViews.Self.class);
ResponseBean rb = new ResponseBean();
try {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(username, "",userDetails.getAuthorities());
//Authentication authentication = this.authManager.authenticate(authenticationToken);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
/*
* Reload user as password of authentication principal will be null after authorization and
* password is needed for token generation
*/
// String ip = request.getRemoteAddr();
// String token = TokenUtils.createToken(userDetails , ip);
try {
UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
LoginLog ll = new LoginLog();
ll.setUserId(((User) userDetails).getId());
ll.setIpAddress(request.getRemoteAddr());
ll.setLoginTime(new Date());
ll.setBrowser(userAgent.getBrowser().getName());
ll.setDevice(userAgent.getOperatingSystem().getDeviceType().getName());
loginLogDao.save(ll);
} catch (Exception e) {
logger.error("fail to save login log", e);
e.printStackTrace();
}
//ADD TO SESSION
request.getSession().setAttribute(Constants.Authentication.CURRENT_USER, userDetails);
List<Map> menuList = new ArrayList<Map>();
if (type != null && !"".equals(type)) {
User user=(User) userDetails;
menuList = menuDao.findByUser(user, type);
}
rb.setData(MapUtils.asMap(MapUtils.any("user", userDetails), MapUtils.any("menus", menuList)));//MapUtils.any("token", token),
String userinfo = URLEncoder.encode(viewWriter.writeValueAsString(userDetails), "utf-8");
Cookie cookie=new Cookie("user",userinfo);
cookie.setPath("/");
response.addCookie(cookie);
} catch (Exception e) {
logger.error("faile to login", e);
rb.setMessage(100001, "username or password is invalid.");
}finally {
rb.setData(true);
response.getWriter().print(rb);//返回響應資訊
}
}
3.讀取使用者的微信繫結狀態
/**
* 讀取使用者的微信繫結狀態
*
* @author simon
* @date 2016/02/25
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("weichatState")
public ResponseBean weichatState() {
ResponseBean responseBean = new ResponseBean();
try{
//根據當前登陸的使用者id找到對應的繫結表的資訊
WeichatBind weichatBind = userService.getByUserId(LuoboAppContext.currentUser().getId());
if(weichatBind!=null)
responseBean.setData(weichatBind);
else
responseBean.setErrorCode(-1);
}catch (Exception e){
responseBean.setErrorCode(-1);
}
return responseBean;
}
4.使用者取消繫結微訊號
/**
* 使用者取消繫結微訊號
*
* @author simon
* @date 2016/02/25
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("unbind")
public ResponseBean UnBindWeixin() {
ResponseBean responseBean = new ResponseBean();
//根據當前登陸的使用者id找到對應的繫結表的資訊
try {
WeichatBind weichatBind = userService.getByUserId(LuoboAppContext.currentUser().getId());
userService.removeBind(weichatBind.getId());
}catch (Exception e){
responseBean.setErrorCode(-1);
}
return responseBean;
}
5.使用者繫結手機號
/**
* @描述 使用者繫結手機號
*
* @param mobileNo 繫結的手機號
* @param type PC 或 Mobile
*
* @author simon
* @date 2016/02/29
*/
@GET
@Path("bind")
public void BindWeixin(@QueryParam("mobileNo") Long mobileNo,
@Context HttpServletRequest request,
@Context HttpServletResponse response,
@QueryParam("type") String type) throws Exception {
//1.根據要繫結的手機號資訊找對應的user資訊
User user = userService.getUserByMobileNo(mobileNo);
//2.從session獲得在上一步中放入
Map<String, Object> userinfoMap = (Map<String, Object>)request.getSession().getAttribute("userinfoMap");
if(userinfoMap==null){
ResponseBean responseBean=new ResponseBean();
responseBean.setErrorCode(-2);
response.getWriter().print(responseBean);//出現異常
return;
}
if(user==null){//使用者不存在,要生成賬號
User newuser=new User();
newuser.setMobileNo(mobileNo);
newuser.setName(userinfoMap.get("nickname").toString());
newuser.setEnabled(true);
newuser.setStatus("1");
if(type.equals("PC")){
newuser.setRegDevice("01");
}else{
newuser.setRegDevice("02");
}
newuser.setUsername(""+mobileNo);
String password=CodeUtils.generateRandomString(6);//隨機密碼
newuser.setPassword(this.passwordEncoder.encode(password));
newuser.addRole(Constants.Role.USER);
user= this.userDao.save(newuser);//生成賬號
//微信登陸註冊成功計算獲得積分
try {
int obtainPoints = pointsService.calculatePointsForUserRegister(user.getId());
} catch (Exception e) {
logger.error("error occurs: ", e);
// 記錄錯誤日誌
}
// 註冊成功的同時,新增一個對應的簡歷記錄
MicroCv cv = new MicroCv();
cv.setUserId(user.getId());
cv.setName(user.getName());
cv.setPhone(user.getMobileNo());
cv.setEmail(user.getEmail());
cv.setIsSelf(true);
microCvDao.save(cv);
//註冊成功的同時,要新增一個對應的積分記錄
//如果註冊時帶了邀請碼,則給邀請人加積分
// if (!StringUtils.isEmpty(bean.getToken())) {
// try {
// pointsService.calculatePointsForInviteRegister(bean.getToken(), user.getName(), String.valueOf(user.getMobileNo()));
// } catch (Exception e) {
// logger.error("error occurs: ", e);
// // 記錄錯誤日誌
// }
// }
WeichatBind weichatBind = new WeichatBind();
weichatBind.setUnionid(userinfoMap.get("unionid").toString());
weichatBind.setUserId(user.getId());
//完成繫結
WeichatBind weichatBind1=userService.createBind(weichatBind);
//完成模擬登陸
autoLogin(request,response,user.getUsername(),type);
}else{
//已經存在,找到賬號完成繫結,再模擬登陸
WeichatBind weichatBind = new WeichatBind();
weichatBind.setUnionid(userinfoMap.get("unionid").toString());
weichatBind.setUserId(user.getId());
WeichatBind weichatBind1=userService.createBind(weichatBind);//3.完成繫結
//完成模擬登陸
if(type.equals("PC")){
autoLogin(request,response,user.getUsername(),"PC");
}else{
autoLogin(request,response,user.getUsername(),null);
}
}
}
6.與微信平臺互動的程式碼
package com.bigluobo.utils;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import java.io.*;
import java.net.*;
import java.util.*;
/**
* Created by Henry on 2015/12/15.
*/
public class CodeUtils {
private static final String appid="******************";
private static final String secret="******************";
//獲得隨機值
public static final String generateRandomString(int length) {
String allChar = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
StringBuffer sb = new StringBuffer();
Random random = new Random();
for (int i = 0; i < length; i++) {
sb.append(allChar.charAt(random.nextInt(allChar.length())));
}
return sb.toString();
}
/**
* 通過code向微信開放平臺請求access_token
*
* @param code
*
*/
public static Map<String,Object> get_access_token(String code) {
//拼接請求access_token的連結
String url = "https://api.weixin.qq.com/sns/oauth2/access_token";
String params="appid="+appid+"&secret="+secret+"&code="+
code+"&grant_type=authorization_code";
String resultJson = sendGet(url, params);
Map<String,Object> map = parseData(resultJson);//將返回的json資料轉換為Map結構儲存
/*示例:
*{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE",
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
* */
return map;
}
/**
* 函式名稱: refresh_access_token
*
* 函式描述: access_token超時,使用refresh_token重新獲得一個access_token
*
* @param refresh_token
* @return Map<String, String>
*/
public static Map<String,Object> refresh_access_token(String refresh_token){
//access_token超時,此時需要重新獲得一個access_token。
String url_access="https://api.weixin.qq.com/sns/oauth2/refresh_token";
StringBuffer params_access=new StringBuffer()
.append("appid=").append(appid)
.append("&grant_type=refresh_token")
.append("&refresh_token=").append(refresh_token);
String resultJson=sendGet(url_access,params_access.toString());
Map<String,Object> map = parseData(resultJson);//將返回的json資料轉換為Map結構儲存
/*
* {
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE"
}
* */
return map;
}
/**
* 函式名稱: get_userinfo
*
* 函式描述: 通過access_token去獲取使用者的資訊
*
* @param access_token
* @return Map<String, String>
*/
public static Map<String,Object> get_userinfo(String access_token,String openid){
//access_token超時,此時需要重新獲得一個access_token。
String url_userinfo="https://api.weixin.qq.com/sns/userinfo";
StringBuffer params_userinfo=new StringBuffer()
.append("access_token=").append(access_token)
.append("&openid=").append(openid);
String resultJson=sendGet(url_userinfo,params_userinfo.toString());
Map<String,Object> map = parseData(resultJson);//將返回的json資料轉換為Map結構儲存
if(map.size()>3){
//已經正確獲取到使用者資訊
/*
* {
"openid":"OPENID",
"nickname":"NICKNAME",
"sex":1,
"province":"PROVINCE",
"city":"CITY",
"country":"COUNTRY",
"headimgurl": "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
"privilege":[
"PRIVILEGE1",
"PRIVILEGE2"
],
"unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
* */
return map;
}else{
if(map.get("errcode").equals("42001")){
//access_token超時,需要重新獲得access_token超時,再請求使用者資訊
Map<String,Object> map1= refresh_access_token(access_token);
String access_token2=map1.get("access_token").toString();
String openid2=map1.get("openid").toString();
//重新整理以後重新獲取使用者的資訊
get_userinfo(access_token2,openid2);
}
}
return map;
}
/**
* 函式名稱: parseData
* 函式描述: 將json字串轉換為Map<String, String>結構
*
* @param data
* @return Map<String, String>
*/
private static Map<String, Object> parseData(String data) {
GsonBuilder gb = new GsonBuilder();
Gson g = gb.create();
Map<String, Object> map = g.fromJson(data, new TypeToken<Map<String, Object>>() {
}.getType());
return map;
}
/**
* 向指定URL傳送GET方法的請求
*
* @param url 傳送請求的URL
* @param param 請求引數,請求引數應該是 name1=value1&name2=value2 的形式。
* @return URL 所代表遠端資源的響應結果
*/
public static String sendGet(String url, String param) {
String result = "";
BufferedReader in = null;
try {
String urlNameString = url + "?" + param;
URL realUrl = new URL(urlNameString);
// 開啟和URL之間的連線
URLConnection connection = realUrl.openConnection();
// 設定通用的請求屬性
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 建立實際的連線
connection.connect();
// 獲取所有響應頭欄位
Map<String, List<String>> map = connection.getHeaderFields();
// 定義 BufferedReader輸入流來讀取URL的響應
in = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("傳送GET請求出現異常!" + e);
e.printStackTrace();
}
// 使用finally塊來關閉輸入流
finally {
try {
if (in != null) {
in.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return result;
}
}