原创

CAS Server集成QQ登录、新浪微博登录源码及配置文件

CAS Server集成QQ第三方登录,CAS Server集成新浪微博第三方登录以及CAS Server集成微信网页登录源码和配置基本类似,本文着重介绍一下如何通过CAS Server(cas-server3.5.2)实现qq第三方登录,并分享一下实现代码和具体配置文件内容,虽然包含新浪微博登录但是未经测试,有什么疑问可留言。


首先简单介绍一下CAS Server,它是一套基于Java实现的服务,该服务以一个Java Web Application单独部署在与servlet2.3兼容的Web服务器上。由于CAS ClientCAS Server之间的交互采用Https协议,因此部署CAS Server的服务器还需要支持SSL协议。当SSL配置成功过后,像普通Web应用一样将CAS Server部署在服务器上就能正常运行了,也可通过手动修改配置取消对SSL协议的支持。


cas-server集成QQ第三方登录

1、打开cas-server工程中cas-server-support-oauth子项目,创建QQApi20.java类文件名称并继承DefaultApi20.java类,具体代码如下:

package org.jasig.cas.support.oauth.qq;
import org.jasig.cas.support.oauth.OAuthConstants;
import org.scribe.builder.api.DefaultApi20;
import org.scribe.model.OAuthConfig;
import org.scribe.utils.OAuthEncoder;
public class QQApi20 extends DefaultApi20 {
    private static String serverUrl = "";
    
    @Override
    public String getAccessTokenEndpoint() {
        return serverUrl + "/" + OAuthConstants.QQ_ACCESS_TOKEN_URL + "?grant_type=authorization_code";
    }
    
    @Override
    public String getAuthorizationUrl(final OAuthConfig config) {
        return String.format(serverUrl + "/" + OAuthConstants.AUTHORIZE_URL
        + "?client_id=%s&redirect_uri=%s&response_type=code", config.getApiKey(),
        OAuthEncoder.encode(config.getCallback()));
    }
    
    public static void setServerUrl(final String url) {
        serverUrl = url;
    }
}


2、创建QQProvider.java类文件名称并继承BaseOAuth20Provider.java类,具体代码如下:

package org.jasig.cas.support.oauth.qq;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.jasig.cas.support.oauth.OAuthConstants;
import org.jasig.cas.support.oauth.entity.UserInfo;
import org.jasig.cas.support.oauth.profile.QQWrapperProfile;
import org.scribe.builder.ServiceBuilder;
import org.scribe.model.Token;
import org.scribe.up.profile.JsonHelper;
import org.scribe.up.profile.UserProfile;
import org.scribe.up.provider.BaseOAuth20Provider;
import org.scribe.up.provider.BaseOAuthProvider;
import org.springframework.orm.jpa.JpaTemplate;

import com.fasterxml.jackson.databind.JsonNode;

/**
 * This class is the OAuth provider to authenticate user in CAS server wrapping OAuth protocol.
 * 
 * @author Jerome Leleu
 * @since 3.5.0
 */
public final class QQProvider extends BaseOAuth20Provider {
    
    private String serverUrl;
    
    private JpaTemplate jpaTemplate;
    
    @Override
    protected void internalInit() {
    	QQApi20.setServerUrl(this.serverUrl);
        this.service = new ServiceBuilder().provider(QQApi20.class).apiKey(this.key).apiSecret(this.secret)
            .callback(this.callbackUrl).build();
    }
    
    @Override
    protected String getProfileUrl() {
        return this.serverUrl + "/" + OAuthConstants.QQ_PROFILE_URL;
    }
    
	@Override
    protected UserProfile extractUserProfile(final String body) {
    	
        final QQWrapperProfile userProfile = new QQWrapperProfile();
        String str = body.replace("callback( ", "").replace(" );", "");
        JsonNode json = JsonHelper.getFirstNode(str);
		userProfile.setId(JsonHelper.get(json, QQWrapperProfile.ID));
		userProfile.addAttribute(QQWrapperProfile.CLIENTID, JsonHelper.get(json, QQWrapperProfile.CLIENTID));
        return userProfile;
    }
	
	private static String getRandomStr(){
		SimpleDateFormat formatter = new SimpleDateFormat ("MMddHHmmss");
		Date curDate = new Date(System.currentTimeMillis());//获取当前时间
		String str = formatter.format(curDate);
		String cur = str.substring(0,2);
		String cur2 = str.substring(2,4);
		String temp = (Integer.parseInt(cur)+Integer.parseInt(cur2))+""+str.substring(4);
		int cur_id = Integer.parseInt(temp.substring(0,4))+Integer.parseInt(temp.substring(4));
		String randomstr ="y" + cur_id + (int)(Math.random()*10000);
		return randomstr;
	}
   
	@SuppressWarnings({ "deprecation", "rawtypes" })
    @Override
    protected UserProfile getUserProfile(final Token accessToken) {
        final String body = sendRequestForData(accessToken, getProfileUrl());
        if (body == null) {
            return null;
        }
        final UserProfile profile = extractUserProfile(body);
        addAccessTokenToProfile(profile, accessToken);
        String url = "https://graph.qq.com/user/get_user_info?access_token="+accessToken.getToken()+"&oauth_consumer_key="+this.key+"&openid="+profile.getId();
        String response = getHttp(url);
        JsonNode json = JsonHelper.getFirstNode(response);
        String ret = json.get("ret").asText();
        if(ret != null && ret.equals("0")){
        	List list = jpaTemplate.find("from UserInfo where openid='"+profile.getId()+"'");
    		UserInfo userInfo = null;
    		if(list == null || list.isEmpty()){
    			userInfo = new UserInfo();
    			String nickName = json.get("nickname").asText();
    			nickName = nickName.replaceAll("[^0-9a-zA-Z\\u4e00-\\u9fa5]", "");
    			userInfo.setNickName(nickName);
    			userInfo.setAvatar(json.get("figureurl_qq_1").asText());
    			userInfo.setGender(json.get("gender").asText());
    			userInfo.setOpenId(profile.getId());
    			userInfo.setClientId((String)profile.getAttributes().get(QQWrapperProfile.CLIENTID));
    			userInfo.setUserName(getRandomStr());
    			userInfo.setCompleted(0);
    			if(userInfo.getNickName() == null || userInfo.getNickName().equals("")){
    				userInfo.setNickName(userInfo.getUserName());
    			}
    			userInfo.setCreateTime(new Date());
    			jpaTemplate.persist(userInfo);
    		}else{
    			userInfo = (UserInfo) list.get(0);
    			userInfo.setUpdateTime(new Date());
    			jpaTemplate.merge(userInfo);
    		}
    		profile.setId(userInfo.getUserName());
    		profile.addAttribute("userId",userInfo.getUserId());
    		profile.addAttribute("userName",userInfo.getUserName());
            profile.addAttribute("nickName",userInfo.getNickName());
            profile.addAttribute("avatar",userInfo.getAvatar());
            profile.addAttribute("gender",userInfo.getGender());
            profile.addAttribute("openId",userInfo.getOpenId());
            profile.addAttribute("clientId",userInfo.getClientId());
            profile.addAttribute("completed",userInfo.getCompleted());
        }
        return profile;
    }
    
    public static String getHttp(String url) {
		String responseMsg = "";
		HttpClient httpClient = new HttpClient();
		GetMethod getMethod = new GetMethod(url);
		getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,new DefaultHttpMethodRetryHandler());
		try {
			httpClient.executeMethod(getMethod);
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			InputStream in = getMethod.getResponseBodyAsStream();
			int len = 0;
			byte[] buf = new byte[1024];
			while((len=in.read(buf))!=-1){
				out.write(buf, 0, len);
			}
			responseMsg = out.toString("UTF-8");
		} catch (HttpException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			//释放连接
			getMethod.releaseConnection();
		}
		return responseMsg;
	}
    
    public void setServerUrl(final String serverUrl) {
        this.serverUrl = serverUrl;
    }
    
    @Override
    protected BaseOAuthProvider newProvider() {
        final QQProvider newProvider = new QQProvider();
        newProvider.setServerUrl(this.serverUrl);
        return newProvider;
    }

	public JpaTemplate getJpaTemplate() {
		return jpaTemplate;
	}

	public void setJpaTemplate(JpaTemplate jpaTemplate) {
		this.jpaTemplate = jpaTemplate;
	}
    
    
}


3、在cas-server-webapp子项目中,打开applicationContext.xml文件,添加对应的oauth的clients信息并把clients增加到对应的org.pac4j.core.client.Clients中,具体配置内容下:

<bean id="sinaWeibo" class="org.jasig.cas.support.oauth.sina.SinaWeiboProvider">
        <property name="key" value="xxxxxxxxxxxxxxxx" />  
        <property name="secret" value="xxxxxxxxxxxxxxxxxxxxxxxxxxxx" />
        <property name="serverUrl" value="https://api.weibo.com/oauth2"/>
        <property name="callbackUrl" value="http://api.yoodb.com" />  <!-- oauth10login -->
</bean>
<bean id="qq" class="org.jasig.cas.support.oauth.qq.QQProvider"><!--新浪微博bean配置,跳过-->
    <property name="jpaTemplate" ref="jpaTemplate"/>
    <property name="key" value="xxxxxxxxxxxx" />  
    <property name="secret" value="xxxxxxxxxxxxxxxxxxxx" />
    <property name="serverUrl" value="https://graph.qq.com/oauth2.0"/>
    <property name="callbackUrl" value="http://api.yoodb.com" />  <!-- oauth10login -->
</bean>

 

4、在cas-server-webapp子项目中,打开deployerConfigContext.xml文件,增加bean配置,具体配置内容下:

<bean class="org.jasig.cas.support.oauth.OAuthConfiguration" id="oAuthConfiguration">
<property name="loginUrl" value="http://api.yoodb.com"/>
<property name="providers">         
            <list>           
                <ref bean="sinaWeibo" /><!--新浪微博配置,跳过-->
                <ref bean="qq" />                       
            </list>       
        </property>
</bean>


5、添加以下链接到登录页面casLoginView.jsp(ClientNameUrl这个属性是被OAuthConfiguration自动创建),也就是你自定义的Client类名加上Url。如我创建的类为QQProvider则对应的link名为QQProviderUrl,具体代码如下:

<div class="row btn-row third-part">
                     	<p class="third_login"><span>第三方帐号登录</span></p>
                     	<a href="${QQProviderUrl}" class="qq"></a>
                      	<a href="javascript:alert('目前只有QQ登录可以使用!');" class="sina"></a> 
<%--                       	<a href="${SinaWeiboProviderUrl}" class="sina"></a>  --%>
                      	<a href="javascript:alert('目前只有QQ登录可以使用!');" class="baidu"></a>
                      	<a href="javascript:alert('目前只有QQ登录可以使用!');" class="wechat"></a>
                     </div>


cas-server集成新浪微博第三方登录

1、打开cas-server工程中cas-server-support-oauth子项目,创建SinaWeiboApi20.java类文件名称并继承DefaultApi20.java类,具体代码如下:

package org.jasig.cas.support.oauth.sina;

import org.jasig.cas.support.oauth.OAuthConstants;
import org.scribe.builder.api.DefaultApi20;
import org.scribe.model.OAuthConfig;
import org.scribe.utils.OAuthEncoder;

public class SinaWeiboApi20 extends DefaultApi20 {
	private static String serverUrl = "";

	@Override
	public String getAccessTokenEndpoint() {
		return serverUrl + "/" + OAuthConstants.SINA_ACCESS_TOKEN_URL + "?";
	}

	@Override
	public String getAuthorizationUrl(final OAuthConfig config) {
		return String.format(serverUrl + "/" + OAuthConstants.AUTHORIZE_URL
				+ "?client_id=%s&redirect_uri=%s&response_type=code", config.getApiKey(),
				OAuthEncoder.encode(config.getCallback()));
	}

	public static void setServerUrl(final String url) {
		serverUrl = url;
	}
}


2、创建SinaWeiboProvider.java类文件名称并继承BaseOAuth20Provider.java类,具体代码如下:

package org.jasig.cas.support.oauth.sina;

import java.util.Iterator;

import org.jasig.cas.support.oauth.OAuthConstants;
import org.jasig.cas.support.oauth.profile.CasWrapperProfile;
import org.scribe.builder.ServiceBuilder;
import org.scribe.up.profile.JsonHelper;
import org.scribe.up.profile.UserProfile;
import org.scribe.up.provider.BaseOAuth20Provider;
import org.scribe.up.provider.BaseOAuthProvider;

import com.fasterxml.jackson.databind.JsonNode;

/**
 * This class is the OAuth provider to authenticate user in CAS server wrapping OAuth protocol.
 * 
 * @author Jerome Leleu
 * @since 3.5.0
 */
public final class SinaWeiboProvider extends BaseOAuth20Provider {
    
    private String serverUrl;
    
    @Override
    protected void internalInit() {
    	SinaWeiboApi20.setServerUrl(this.serverUrl);
        this.service = new ServiceBuilder().provider(SinaWeiboApi20.class).apiKey(this.key).apiSecret(this.secret)
            .callback(this.callbackUrl).build();
    }
    
    @Override
    protected String getProfileUrl() {
        return this.serverUrl + "/" + OAuthConstants.SINA_PROFILE_URL;
    }
    
    @Override
    protected UserProfile extractUserProfile(final String body) {
        final CasWrapperProfile userProfile = new CasWrapperProfile();
        JsonNode json = JsonHelper.getFirstNode(body);
        if (json != null) {
            userProfile.setId(JsonHelper.get(json, CasWrapperProfile.ID));
            json = json.get(CasWrapperProfile.ATTRIBUTES);
            if (json != null) {
                final Iterator<JsonNode> nodes = json.iterator();
                while (nodes.hasNext()) {
                    json = nodes.next();
                    final String attribute = json.fieldNames().next();
                    userProfile.addAttribute(attribute, JsonHelper.get(json, attribute));
                }
            }
        }
        return userProfile;
    }
    
    public void setServerUrl(final String serverUrl) {
        this.serverUrl = serverUrl;
    }
    
    @Override
    protected BaseOAuthProvider newProvider() {
        final SinaWeiboProvider newProvider = new SinaWeiboProvider();
        newProvider.setServerUrl(this.serverUrl);
        return newProvider;
    }
}

cas-server集成新浪微博登录和在cas-server集成qq登录类似,参考一下(本文中已经增加新浪微博配置及步骤但未经过测试),此处省略。

关注下方微信公众号“Java精选”(w_z90110),回复关键字领取资料:如HadoopDubboCAS源码等等,免费领取资料视频和项目。 

涵盖:程序人生、搞笑视频、算法与数据结构、黑客技术与网络安全、前端开发、Java、Python、Redis缓存、Spring源码、各大主流框架、Web开发、大数据技术、Storm、Hadoop、MapReduce、Spark、elasticsearch、单点登录统一认证、分布式框架、集群、安卓开发、iOS开发、C/C++、.NET、Linux、Mysql、Oracle、NoSQL非关系型数据库、运维等。

评论

  1. #1

    陈小胖 (2018/06/25 09:30:55)回复
    cas-server-support-oauth 子项目是啥意思呀 ??方便加下qq不

    路人甲 (2018/06/26 11:22:00)回复
    QQ号多少,我加你一下,或者加我微信。在我的网站首页就有。

  2. #2

    杰克 (2018/05/08 10:05:59)回复
    找不到DefaultApi20这个类,需要引入哪个jar包了,我的qq是903829182,可以告知一下吗?

    路人甲 (2018/05/21 12:25:27)回复
    这个在我的公众号有源码,你可以自己去下载了。

  3. #3

    D虚伪 (2018/03/01 11:24:09)回复
    我这个 QQProviderUrl 为空啊 能加上您的qq求教一下吗

    路人甲 (2018/03/05 11:34:44)回复
    你加我好友就行了,网站上就有。

  4. #4

    大哥哥你好 (2017/10/13 16:16:26)回复
    你好 请问下用的cas哪个版本?

    路人甲 (2017/10/13 16:37:32)回复
    我用的是cas-server3.5.2版本的服务端,也用过cas-server-4.0.0版本的服务端,上面文章用的是3.5.2有什么疑问吗?!

  5. #5

    无极天尊 (2017/10/13 16:01:28)回复
    厉害了, 源码 分享 下 最好了 。。哈哈

  6. #6

    大雄NO.1 (2017/10/10 14:16:56)回复
    直接把CAS Server项目源码放上来不更好吗,方便大家?

    路人甲 (2017/10/10 14:30:48)回复
    项目源码太多,即使放了又跑不起来的,集成了很多中间件比如Redis缓存,Resuful接口,oauth2.0第三方登录服务等,太多比较杂。

分享:

支付宝

微信