【SSO单点登录】7.完全跨域-第三方登录详解

本文章由聚项云平台博客独家原创,转载请注明出处:http://blog.jxcode.com/?p=2957&preview=true

之前我们编写了一个跨子域的单点登录的实例,并且了解了实现跨子域单点登录的大致方向。那么对于跨域的单点登录呢?其实日常上网中遇到的第三方登录就是一个完全跨域的单点登录。为了让大家对后面的协议实现跨域单点登录更加的了解,我们以目前比较火的第三方(QQ、微信、微博)登录作为例子,向大家阐述一下其中的原理。

例如我们登录一个娱乐网站,它要求我们登录,并在下面提供了第三方登录的连接:
图7.1
以其中QQ登录为例,其整个登录认证过程如下:
图7.2
首先用户登录XXX网站,然后通过该网站的第三方登录按钮跳转至QQ授权页面,用户在QQ授权页面填写QQ登录信息后,提交至QQ认证服务器,接着认证服务器判断用户账号密码是否正确,如果正确,返回授权成功页面,然后自动跳转回XXX网站。如果登录失败,则跳转至QQ登录失败界面。
如果XXX网站还有子域系统,它的子域系统会带着用户从QQ带来的凭证信息去QQ的认证服务器校验,如果通过了则证明已经获得了QQ认证服务器的认证,则会自动登录。

光说不练假把式,我们从代码角度来实现QQ的第三方登录。下面是QQ的第三方登录的授权流程图:
图7.3
首先用户登录某接入第三方登录的网站,点击第三方登录后,首先到授权页面,授权页面会将用户身份ID和认证成功后的重定向URI地址发向QQ认证服务器,并且包含用户登录时输入的认证信息,QQ认证服务器校验登录信息后,返回给授权页面授权码,标识授权结果,然后接入网站带着授权码和认证成功后的重定向URI地址直接请求QQ认证服务器,服务器会根据认证码查询当前用户的授权凭证是否有效或过期,如果有效,则返回给接入网站一个唯一的Token信息。此时用户登录成功后,就可以访问资源了。

对于用户而言,只发生了A、B、C三件事情。
对于接入网站而言,认证需要进行两步:
1. 获取Authorization Code;
2. 通过Authorization Code获取Access Token

代码实现需要进行以下6步:
(1)接入网站访问QQ授权登录地址:https://graph.qq.com/oauth2.0/authorize
请求方式:GET方式。
参数(这里只列出必须的):
response_type:授权类型,此值固定为“code”。
client_id:申请QQ登录成功后,分配给应用的appid。
redirect_uri:成功授权后的回调地址,必须是注册appid时填写的主域名下的地址,建议设置为网站首页或网站的用户中心。注意需要将url进行URLEncode。

(2)通过第一步后,接入网站会获得一个Authorization Code认证码(此code10分钟内过期)

(3)使用Authorization Code获取Access Token(PC网站通过地址:https://graph.qq.com/oauth2.0/token)
请求方式:GET方式。
请求参数:
grant_type:授权类型,在本步骤中,此值为“authorization_code”。
client_id:申请QQ登录成功后,分配给网站的appid。
client_secret:申请QQ登录成功后,分配给网站的appkey。
code:上一步返回的Authorization code。
如果用户成功登录并授权,则会跳转到指定的回调地址,并在URL中带上Authorization Code。
例如,回调地址为www.qq.com/my.php,则跳转到:
http://www.qq.com/my.php?code=520DD95263C1CFEA087******
注意此code会在10分钟内过期。
redirect_uri:与上面一步中传入的redirect_uri保持一致。

(4)此时拿到了Access Token后,获取用户的OpenID_OAuth2.0
通过地址:https://graph.qq.com/oauth2.0/token获取
请求方式:GET方式。
请求参数:
grant_type:授权类型,在本步骤中,此值为“refresh_token”。
client_id:申请QQ登录成功后,分配给网站的appid。
client_secret:申请QQ登录成功后,分配给网站的appkey。
refresh_token:在Step2中,返回的refres_token。

该服务会返回类似以下的结果:
callback( {“client_id”:”YOUR_APPID”,”openid”:”YOUR_OPENID”} );

接着跳转回接入网站页面,此时可通过调用get_user_info接口,获得该用户的头像、昵称并显示在网站上,使用户体验统一:
发送请求到get_user_info的URL(请将access_token,appid等参数值替换为你自己的):
https://graph.qq.com/user/get_user_info?access_token=YOUR_ACCESS_TOKEN&oauth_consumer_key=YOUR_APP_ID&openid=YOUR_OPENID
成功返回后,即可获取到用户数据:
{
“ret”:0,
“msg”:””,
“nickname”:”YOUR_NICK_NAME”,

}

要使用QQ的第三方登录服务,我们首先要去QQ互联(https://connect.qq.com/)申请
第三方登录的开发者信息验证:
图7.4

然后点击头像输入审核信息:
图7.5

然后进行资料填写,填写你网站的域名、回调地址、网站图标等。

完成审核后,就会获得QQ登录的三个参数:appid、appkey、回调地址。
在编写的Java代码中,如果是maven工程,则要在POM文件中引入以下依赖配置:

 <dependency>
    <groupId>net.gplatform</groupId>
    <artifactId>Sdk4J</artifactId>
    <version>2.0</version>
 </dependency>

实际上就是引入sdk4j,即QQ为开发者提供的Java版认证的jar包。

下面是一个springmvc的样例,当我们在自己的网站上提供一个第三方登录按钮,点击后跳转到QQ登录页面,则需要编写一个处理跳转到第三方登录的请求:

 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import org.springframework.stereotype.Component;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.servlet.ModelAndView;

import com.qq.connect.QQConnectException;
 import com.qq.connect.oauth.Oauth;

@Component
 @RequestMapping("/qq.do")
 public class qqloginController {

    @RequestMapping
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        response.setContentType("text/html;charset=utf-8");
        try {
             response.sendRedirect(new Oauth().getAuthorizeURL(request));
        } catch (QQConnectException e) {
            e.printStackTrace();
        }

        return null;
    }
 }

其中调用response.sendRedirect(new Oauth().getAuthorizeURL(request));会将页面重定向到qq第三方的登录页面。

然后编写在第三方登录成功后进行登录信息获取的服务,当第三方认证成功后会跳转至我们配置的redirect_URI的地址上,该地址的服务就是我们要编写的获取用户信息的服务,样例代码:

 import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.servlet.ModelAndView;

import com.qq.connect.api.OpenID;
 import com.qq.connect.api.qzone.UserInfo;
 import com.qq.connect.javabeans.AccessToken;
 import com.qq.connect.javabeans.qzone.UserInfoBean;
 import com.qq.connect.oauth.Oauth;

@Component
 @RequestMapping("/afterlogin.do")
 public class afterlogin {

    @RequestMapping
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {

        try {
            AccessToken accessTokenObj = (new Oauth()).getAccessTokenByRequest(request);
            String accessToken = null,
            openID = null;
            long tokenExpireIn = 0L;
            if (accessTokenObj.getAccessToken().equals("")) {
               System.out.print("没有获取到响应参数");
            }else{
                accessToken = accessTokenObj.getAccessToken();
                tokenExpireIn = accessTokenObj.getExpireIn();
               OpenID openIDObj = new OpenID(accessToken);
               openID = openIDObj.getUserOpenID();
               UserInfo qzoneUserInfo = new UserInfo(accessToken, openID);
               UserInfoBean userInfoBean = qzoneUserInfo.getUserInfo();
              String name = userInfoBean.getNickname();
               System.out.println("欢迎你," + name + "!");

           }

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

其中当通过getAccessTokenByRequest获取的Token值为空的时候,说明用户认证失败,此时给出提示。如果Token值存在,就可以获取openId和一些用户的信息。

认证成功后,就可以允许用户登录自己的网站,而且可以为用户设置session,在一定时间内避免重复认证,待session会话失效后,重新让用户去第三方登录认证。

未经允许不得转载:JX BLOG » 【SSO单点登录】7.完全跨域-第三方登录详解

赞 (3)

评论 0

评论前必须登录!

登陆 注册