扩展使用 OAuth2协议的账户 |
SuperMap iServer 提供了 OAuth2Client 接口,用于遵循 OAuth2 协议的第三方登录方式扩展,这种第三方登录方式有 QQ、新浪微博等,其中 QQ 和新浪微博为 SuperMap iServer 内置的两种遵循 OAuth2 协议的第三方登录方式。
com.supermap.services.security.OAuth2Client 接口有如下方法:
该方法用于获取 OAuth 跳转 URI 。
该方法用于获取访问 Token 。
该方法用于获取用户 ID 。
该方法用于获取用户信息。
该方法用于登出。
我们通过实现一个简单的扩展,来说明遵循 OAuth2 协议的第三方登录方式的扩展流程,以扩展“微博账户登录”方式为例。
实现了 WeiboOAuth2ClientExtended 类,继承自 OAuth2Client 接口 ,代码如下所示:
package com.supermap.services.security;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
public class WeiboOAuth2ClientExtended implements OAuth2Client {
// 微博认证根地址
private static final String OAUTH_URL_WEIBO = System.getProperty("OAUTH_WEIBO", "https://api.weibo.com");
private static final String GET_CODE_URI_WEIBO = OAUTH_URL_WEIBO + "/oauth2/authorize?client_id=%s&response_type=code&redirect_uri=%s&state=%s";
private static final String USERINFO_URL_PATTERN = OAUTH_URL_WEIBO + "/2/users/show.json?access_token=%s&source=%s&uid=%s";
private static final String EMAIL_URL_PATTERN = OAUTH_URL_WEIBO + "/2/account/profile/email.json?access_token=%s&source=%s";
private static final String GET_TOKEN_BY_CODE_WEIBO = OAUTH_URL_WEIBO + "/oauth2/access_token";
private static final String REVOKE_WEIBO = OAUTH_URL_WEIBO + "/oauth2/revokeoauth2?access_token=%s";
private String userID;
//获取跳转 URI
@Override
public String getRedirectURI(String clientID, String state, String redirectUri) {
return String.format(GET_CODE_URI_WEIBO, clientID, redirectUri, state);
}
//获取访问 Token
@Override
public String getAccesstoken(String clientID, String clientSecret, String code, String redirectUri) {
String tokenUrl = GET_TOKEN_BY_CODE_WEIBO;
List<BasicNameValuePair> nvps = new ArrayList<BasicNameValuePair>();
nvps.add(new BasicNameValuePair("client_id", clientID));
nvps.add(new BasicNameValuePair("client_secret", clientSecret));
nvps.add(new BasicNameValuePair("grant_type", "authorization_code"));
nvps.add(new BasicNameValuePair("code", code));
nvps.add(new BasicNameValuePair("redirect_uri", redirectUri));
String tokenResult = getContentByMethodPost(tokenUrl, nvps);
if (StringUtils.isBlank(tokenResult)) {
return null;
}
JSONObject tokenObj = JSON.parseObject(tokenResult);
userID = tokenObj.getString("uid");
return tokenObj.getString("access_token");
}
// 获取用户 ID
@Override
public String getUserID(String accessToken) {
return userID;
}
//获取用户信息
@Override
public OAuthUserInfo getUserInfo(String token, String clientID, String userID) throws IOException {
String url = String.format(USERINFO_URL_PATTERN, token, clientID, userID);
OAuthUserInfo result = new OAuthUserInfo();
String content;
try {
content = IOUtils.toString(new URI(url), "utf-8");
JSONObject json = JSON.parseObject(content);
if (json != null) {
result.figureurl = json.getString("profile_image_url");
result.nickName = json.getString("name");
result.email = getEmail(token, clientID);
}
return result;
} catch (IOException | URISyntaxException e) {
throw new IOException(e);
}
}
private String getEmail(String token, String clientId) throws IOException {
try {
String url = String.format(EMAIL_URL_PATTERN, token, clientId);
String content = IOUtils.toString(new URI(url), "utf-8");
if (StringUtils.isBlank(content)) {
return StringUtils.EMPTY;
}
int startIndex = StringUtils.indexOf(content, '[');
int endIndex = StringUtils.indexOf(content, ']');
content = StringUtils.substring(content, startIndex + 1, endIndex);
JSONObject json = JSON.parseObject(content);
if (json != null) {
return json.getString("email");
}
} catch (IOException | URISyntaxException e) {
throw new IOException(e);
}
return StringUtils.EMPTY;
}
private String getContentByMethodPost(String url, List<BasicNameValuePair> nvps) {
HttpPost httpPost = new HttpPost(url);
try {
httpPost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
} catch (UnsupportedEncodingException e) {
return null;
}
DefaultHttpClient httpClient = new DefaultHttpClient();
try {
HttpResponse httpResponse = httpClient.execute(httpPost);
if (httpResponse.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = httpResponse.getEntity();
return EntityUtils.toString(entity, HTTP.UTF_8);
}
} catch (IOException ignore) {
} finally {
IOUtils.closeQuietly(httpClient);
}
return null;
}
//登出,此处只实现了调用微博的收回access_token的接口,用户也可以重定向到微博的logout地址,具体代码已注释
@Override
public void logout(ServletRequest request, ServletResponse response, String accessToken) throws IOException {
String url = String.format(REVOKE_WEIBO, accessToken);
try {
// 调用收回access_token接口
IOUtils.toString(new URI(url), "utf-8");
// 重定向至认证中心的登出,销毁Cookies
// ((HttpServletResponse) response).sendRedirect(LOGOUT_WEIBO);
} catch (URISyntaxException e) {
throw new IOException(e);
}
}
}
将编译后包含 WeiboOAuth2ClientExtended.class 文件的整个 com 目录拷贝到 SuperMap iServer Web 应用下,即 %SuperMap iServer_HOME%\webapps\iserver\WEB-INF\classes 下(先在该目录下新建 classes 文件夹)。
在产品包根目录 %SuperMap iServer_HOME%webapps/iserver/WEB-INF 下新建一个 extendedOAuth.xml 文件,里面的内容如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<extendedOAuthSettings>
<extendedOAuthSetting>
<loginType>WEIBO_EXTENDED</loginType>
<oAuth2ClientClass>com.supermap.services.security.WeiboOAuth2ClientExtended</oAuth2ClientClass>
</extendedOAuthSetting>
</extendedOAuthSettings>
其中<extendedOAuthSettings>为遵循 OAuth2 协议的第三方登录方式扩展实现配置集合,可以包含多个<extendedOAuthSetting>标签。每个<extendedOAuthSetting>标签对应一种遵循 OAuth2 协议的第三方登陆方式扩展实现配置,<extendedOAuthSetting>标签中的内容对应于 ExtendedOAuthSetting 中的配置项:
在产品包根目录 %SuperMap iServer_HOME%webapps/iserver/WEB-INF 下的 iserver-system.xml 文件的根节点<server>中添加如下内容:
<server>
...
<oAuthConfigs>
<oAuthConfig>
<id>1</id>
<enabled>true</enabled>
<loginType>WEIBO_EXTENDED</loginType>
<buttonText>微博账号登陆</buttonText>
<clientID>YOUR_CLIENT_ID</clientID>
<clientSecret>YOUR_CLIENT_SECRET</clientSecret>
<redirectDomain>iserver.supermap.com</redirectDomain>
<loginIcon>weibo.png</loginIcon>
</oAuthConfig>
</oAuthConfigs>
<!--<oAuthMetas>
<oAuthMeta><meta property="qc:admins" content="4323423424235" /></oAuthMeta>
</oAuthMetas>-->
</server>
其中<oAuthConfigs>为遵循 OAuth2 协议的第三方登陆方式 配置项集合,可以包含多个<oAuthConfig>标签。每个<oAuthConfig>标签对应一种登陆方式的配置,<oAuthConfig>中的内容对应于 OAuthConfig 中的配置项:
<oAuthMetas>标签代表的是用于验证网站地址的 meta 信息(将添加到首页 HTML 代码的 HEAD 标签中),扩展“微博账号登录”方式不需要填写元数据,在次已将该标签注释掉。
当完成以上步骤时,表示已经成功添加了“微博账号登录“方式。在第三方登陆配置页面(http://iserver.supermap.com:8090/iserver/manager/security/oauthconfig) 可以查看该登陆方式的配置信息,这些信息与<oAuthConfig>标签中的配置项相对应。访问 iPortal、 iServer 或 iEdge 的登陆页面,可以看到登录页面增加了“微博账号登陆”按钮,点击此按钮可以实现用微博账号登录 iPortal、 iServer 或 iEdge 的操作。想了解更多遵循 OAuth2 协议的第三方登录方式的使用,详细请参见:第三方登录方式的使用。