1. 程式人生 > >微信登陸Web應用解決方案

微信登陸Web應用解決方案

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;
    }
}