转载自素文宅博客:https://blog.yoodb.com/yoodb/article/detail/1446
CAS Server
集成QQ第三方登录,CAS Server
集成新浪微博第三方登录以及CAS Server
集成微信网页登录源码和配置基本类似,本文着重介绍一下如何通过CAS Server
(cas-server3.5.2)实现qq第三方登录,并分享一下实现代码和具体配置文件内容,虽然包含新浪微博登录但是未经测试,有什么疑问可留言。
首先简单介绍一下CAS Server
,它是一套基于Java实现的服务,该服务以一个Java Web Application
单独部署在与servlet2.3
兼容的Web
服务器上。由于CAS Client
与CAS 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中,具体配置内容下:
4、在cas-server-webapp
子项目中,打开deployerConfigContext.xml
文件,增加bean配置,具体配置内容下:
5、添加以下链接到登录页面casLoginView.jsp(ClientNameUrl这个属性是被OAuthConfiguration自动创建),也就是你自定义的Client类名加上Url。如我创建的类为QQProvider则对应的link名为QQProviderUrl,具体代码如下:
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 Iteratornodes = 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登录类似,参考一下(本文中已经增加新浪微博配置及步骤但未经过测试),此处省略。