大致說下整個流程:
1. 註冊百度開發者 http://developer.baidu.com/ 獲取key secret等值, 同時定義自己的授權回掉頁。
2. 尋找API, OAuth的相關API在 http://developer.baidu.com/ms/oauth
3. 引導用戶進行授權
4. 根據得到的Code, 進行AccessToken的獲取
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;
}
}