扩展使用 OAuth2协议的账户

发送反馈


SuperMap iServer 提供了 OAuth2Client 接口,用于遵循 OAuth2 协议的第三方登录方式扩展,这种第三方登录方式有 QQ、新浪微博等,其中 QQ 和新浪微博为 SuperMap iServer 内置的两种遵循 OAuth2 协议的第三方登录方式。

OAuth2Client 接口介绍

com.supermap.services.security.OAuth2Client 接口有如下方法:

该方法用于获取 OAuth 跳转 URI 。

该方法用于获取访问 Token 。

该方法用于获取用户 ID 。

该方法用于获取用户信息。

该方法用于登出。

扩展和配置流程

我们通过实现一个简单的扩展,来说明遵循 OAuth2 协议的第三方登录方式的扩展流程,以扩展“微博账户登录”方式为例。

1、“微博账户登录”方式实现类

实现了 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 文件夹)。

2、“微博账户登陆”方式的配置

在产品包根目录 %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 标签中),扩展“微博账号登录”方式不需要填写元数据,在次已将该标签注释掉。

3、查看扩展结果

当完成以上步骤时,表示已经成功添加了“微博账号登录“方式。在第三方登陆配置页面(http://iserver.supermap.com:8090/iserver/manager/security/oauthconfig) 可以查看该登陆方式的配置信息,这些信息与<oAuthConfig>标签中的配置项相对应。访问  iPortal、 iServer 或  iEdge 的登陆页面,可以看到登录页面增加了“微博账号登陆”按钮,点击此按钮可以实现用微博账号登录  iPortal、 iServer 或  iEdge 的操作。想了解更多遵循 OAuth2 协议的第三方登录方式的使用,详细请参见:第三方登录方式的使用