基於OAuth2.0實現第三方授權登陸(百度賬號)

大致說下整個流程:


1. 註冊百度開發者 http://developer.baidu.com/ 獲取key secret等值, 同時定義自己的授權回掉頁。


2. 尋找API, OAuth的相關API在  http://developer.baidu.com/ms/oauth


3. 引導用戶進行授權 

http://openapi.baidu.com/oauth/2.0/authorize?
	response_type=code&
	client_id=YOUR_CLIENT_ID&
	redirect_uri=YOUR_REGISTERED_REDIRECT_URI
   


4. 根據得到的Code, 進行AccessToken的獲取

https://openapi.baidu.com/oauth/2.0/token?
	grant_type=authorization_code&
	code=CODE&
	client_id=YOUR_CLIENT_ID&
	client_secret=YOUR_CLIENT_SECRET&
	redirect_uri=YOUR_REGISTERED_REDIRECT_URI


5. 獲取用戶信息

https://openapi.baidu.com/rest/2.0/passport/users/getInfo?access_token=1.a6b7dbd428f731035f771b8d15063f61.86400.1292922000-2346678-124328 HTTP/1.1

下面貼上核心代碼:


配置文件


<?xml version="1.0" encoding="UTF-8"?>
<config id="baidu">
	<params>
		<param name="clientId" value="6422351" />
		<param name="apiKey" value="i3sM3nVMGv46w4eHLgpSQ4GP" />
		<param name="secretKey" value="Gbw9IjqOVeP63AGQi2O0RaPSkLjMfLto" />
		<param name="scope" value="" />
		<param name="redirectUri" value="http://localhost:8080/third_login/baidu" />
		<param name="apiBaseUrl" value="https://openapi.baidu.com/rest/2.0"></param>
	</params>
	<authUrl>
	<![CDATA[
		http://openapi.baidu.com/oauth/2.0/authorize?response_type=code&client_id=${apiKey}&redirect_uri=${redirectUri}
	]]>
	</authUrl>
	<tokenUrl>
	<![CDATA[
		https://openapi.baidu.com/oauth/2.0/token?grant_type=authorization_code&code=${code}&client_id=${apiKey}&client_secret=${secretKey}&redirect_uri=${redirectUri}
	]]>
	</tokenUrl>
	<userInfoApi>
	<![CDATA[
		${apiBaseUrl}/passport/users/getLoggedInUser?access_token=${accessToken}
	]]>
	</userInfoApi>
	<portraitUrlTemplate>
	<![CDATA[
		http://tb.himg.baidu.com/sys/portrait/item/${portrait}
	]]>
	</portraitUrlTemplate>
</config>


ThirdLoginServlet


    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String plat = getPlatName(request);
        String code = request.getParameter("code");

        User user = OAuthHelper.getUserInfo(plat, code);
        if (user != null) {

            User persistenceUser = UserDao.findUserByThirdId(user.getThirdId());
            // 先判斷third_id對應用戶是否已存在
            if (persistenceUser != null) {
                // 存在表示曾經登錄過,更新信息
                persistenceUser.setName(user.getName());
                UserDao.updateName(persistenceUser);// 更新姓名(暱稱)
            } else {
                // 不存在表示實效使用此第三方賬號登錄本系統,先將用戶信息寫入,相當於註冊的過程
                persistenceUser = UserDao.insertUser(user);
            }

            // 寫頭像文件
            persistenceUser.setPortraitData(user.getPortraitData());
            String basePath = request.getServletContext().getRealPath("/portrait");
            savePortrait(basePath + File.separator + persistenceUser.getId(), persistenceUser);
            

            // 將用戶對象存入session,後結操作可根據session中user信息判斷是否登錄及登錄人信息
            request.getSession().setAttribute("loginUser", persistenceUser);

            // 用戶信息存入session,跳轉到用戶頁
            toSuccessPage(plat, request, response);
        } else {
            // 授權過程出現錯誤,進入錯誤頁
            toErrorPage(plat, request, response);
        }
    }

 OAuthInfo


package demo.third.model;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.fasterxml.jackson.databind.JsonNode;

/**
 * 存儲oauth基本信息的類
 * 
 * @author Administrator
 *
 */
public abstract class OAuthInfo {

    private String authUrl;
    private String tokenUrl;
    private String userInfoApi;
    private String portraitUrlTemplate; // 頭像圖片路徑模板

    private Map<String, String> params = new HashMap<String, String>();

    /**
     * 構造時從配置文件讀取信息
     * 
     * @param fileName
     */
    @SuppressWarnings("unchecked")
    public OAuthInfo(String configFile) {

        InputStream is = null;
        SAXReader reader = new SAXReader();

        try {
            is = new FileInputStream(configFile);
            Document doc = reader.read(is);
            Element rootElement = doc.getRootElement();

            // 獲取所有param,存入params map
            Element paramsElement = rootElement.element("params");
            List<Element> paramElements = paramsElement.elements();
            for (Element paramElement : paramElements) {
                params.put(paramElement.attributeValue("name"), paramElement.attributeValue("value"));
            }

            // 獲取authUrl模板後直接用params完成模板替換,得到替換完成的authUrl
            authUrl = templateHandle(rootElement.element("authUrl").getText(), params);

            // 獲取tokenUrl,替換掉固定模板值
            tokenUrl = templateHandle(rootElement.element("tokenUrl").getText(), params);

            // 獲取userInfoApi
            userInfoApi = templateHandle(rootElement.element("userInfoApi").getText(), params);

            // 頭像路徑模板
            portraitUrlTemplate = templateHandle(rootElement.element("portraitUrlTemplate").getText(), params);
        } catch (FileNotFoundException | DocumentException e) {
            e.printStackTrace();
        }
    }

    /**
     * 從返回的json對象中獲取user信息
     * 
     * @param userNode
     * @return
     */
    public abstract User getUser(JsonNode userNode) throws IOException;

    /**
     * 驗證返回的json對象是否有效,因爲有可能返回的是錯誤信息,此時無效
     * 
     * @param userNode
     * @return
     */
    public abstract boolean userDataValidate(JsonNode userNode);

    /**
     * 引導用戶瀏覽器跳轉到授權頁的路徑
     * 
     * @return
     */
    public String getAuthUrl() {
        return authUrl;
    }

    /**
     * 獲取access token的完整url
     * 
     * @param code
     * @return
     */
    public String getTokenUrl(String code) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("code", code);
        return templateHandle(this.tokenUrl, params);
    }

    /**
     * 獲取用戶api訪問完整路徑
     * 
     * @param accessToken
     * @return
     */
    public String getUserInfoApiUrl(String accessToken) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("accessToken", accessToken);
        return templateHandle(this.userInfoApi, params);
    }

    /**
     * 下載頭像圖片
     * 
     * @param portrait
     * @return
     */
    protected byte[] downloadPortrait(String portrait) {

        Map<String, String> params = new HashMap<String, String>();
        params.put("portrait", portrait);
        String urlstr = templateHandle(portraitUrlTemplate, params);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        try {
            URL url = new URL(urlstr);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.connect();
            InputStream is = conn.getInputStream();

            byte[] buff = new byte[1024];
            int cnt;
            while ((cnt = is.read(buff)) > 0) {
                baos.write(buff, 0, cnt);
            }

            is.close();
            conn.disconnect();

            baos.close();
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 模板替換
     * 
     * @param text
     * @param params2
     * @return
     */
    private String templateHandle(String template, Map<String, String> params) {
        // 示例:
        // template =
        // "http://test.com/auth?client_id=${cid}&redirect_uri=${ruri}&auth_type=code";
        // regex = ${(.+?)},匹配${cid}, ${ruri},匹配後group(0)爲${cid},group(1)爲cid

        // 執行匹配操作
        Pattern regex = Pattern.compile("\\$\\{(.+?)\\}");
        Matcher matcher = regex.matcher(template);

        StringBuffer sb = new StringBuffer(); // 用來緩存替換後結果
        while (matcher.find()) { // 從字符串開頭查找每個匹配項
            String key = matcher.group(1); // 找到每個匹配項後,從匹配值中取出要替換的變量名,如cid
            String replaceVal = params.get(key); // 根據變量名到map中查找要替換的值,如cid的真實值

            if (replaceVal == null) {
                // 未在參數map中找到替換值則不替換此參數,如初次處理tokenUrl時,code變量不需要替換
                continue;
            } else {
                // 用指定值替換匹配部分,如:將${cid}替換成真實cid值xxxx,並將替換後結果緩存入sb,匹配項前面的字符原樣存入
                matcher.appendReplacement(sb, replaceVal);
            }
        }
        // 最後一個匹配項並替換完成後,sb中已存入到最後匹配項位置的所有替換結果
        // 最後匹配項以後的字符還沒有加入,通過以下操作,將剩餘字符加入,完成完整替換過程
        matcher.appendTail(sb);

        return sb.toString();
    }
}

GetUserInfo

public static User getUserInfo(String plat, String code) {

        OAuthInfo info = getInfo(plat);
        final ObjectMapper mapper = new ObjectMapper();

        try {
            String atokenUrl = info.getTokenUrl(code);
            String ret = httpGet(atokenUrl);
            JsonNode retNode = mapper.readTree(ret);
            JsonNode atokenNode = retNode.get("access_token");

            if (atokenNode == null) {
                logError(retNode, plat, code, "access_token");
                return null;
            } else {
                String apiUrl = info.getUserInfoApiUrl(atokenNode.asText());
                ret = httpGet(apiUrl);
                JsonNode userNode = mapper.readTree(ret);

                if (info.userDataValidate(userNode)) {
                    return info.getUser(userNode);
                } else {
                    logError(userNode, plat, atokenNode.asText(), "get_user");
                    return null;
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }

    }


靜態構造方法:


static {

        String configBasePath = OAuthHelper.class.getClassLoader().getResource("../config").getPath();
        //http://openapi.baidu.com/oauth/2.0/authorize?response_type=code&client_id=i3sM3nVMGv46w4eHLgpSQ4GP&redirect_uri=http://localhost:8080/third_login/baidu
        infos.put("baidu", new OAuthInfo(configBasePath + File.separator + "baidu.xml") {
            @Override
            public User getUser(JsonNode userNode) throws IOException {
                User user = new User();
                user.setThirdId("baidu_" + userNode.get("uid").asText());
                user.setName(userNode.get("uname").asText());
                user.setPortraitData(downloadPortrait(userNode.get("portrait").asText()));
                return user;
            }

            @Override
            public boolean userDataValidate(JsonNode userNode) {
                return userNode.get("uid") != null;
            }
        });
        
        infos.put("renren", new OAuthInfo(configBasePath + File.separator + "renren.xml") {
            @Override
            public User getUser(JsonNode userNode) throws IOException {
                System.out.println(userNode);

                userNode = userNode.get("response");
                User user = new User();
                user.setThirdId("renren_" + userNode.get("id").asText());
                user.setName(userNode.get("name").asText());
                
                String portrait = userNode.get("avatar").get(1).get("url").asText();
                user.setPortraitData(downloadPortrait(portrait));
                
                return user;
            }
            
            @Override
            public boolean userDataValidate(JsonNode userNode) {
                return userNode.get("response") != null && userNode.get("response").get("id") != null;
            }
        });
    }




下載頭像


 protected byte[] downloadPortrait(String portrait) {

        Map<String, String> params = new HashMap<String, String>();
        params.put("portrait", portrait);
        String urlstr = templateHandle(portraitUrlTemplate, params);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        try {
            URL url = new URL(urlstr);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.connect();
            InputStream is = conn.getInputStream();

            byte[] buff = new byte[1024];
            int cnt;
            while ((cnt = is.read(buff)) > 0) {
                baos.write(buff, 0, cnt);
            }

            is.close();
            conn.disconnect();

            baos.close();
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章