CAS SSO 4.0.x 集成OAuth(微信登陆示例)
由于公司需要cas集成微信。但是在网上没有找到相应的示例。然后我就跑到官网上去找了一下CAS怎么集成OAuth的。这是基于pac4j-oauth-1.4.1.jar包org.pac4j.oauth.client包中用于oauth 2.0协议与CAS集成类的对应与的。下面是对应的Client,毕竟是人家外国人的jar。没有微信的,那就只有自己写咯。
下面的官网加上我自己的改动。
1、在cas-server-webapp中的pom.xml中加入以下dependency用于支持oauth.
<dependency> <groupId>org.jasig.cas</groupId> <artifactId>cas-server-support-pac4j</artifactId> <version>${cas.version}</version>
</dependency>
2、配置传递属性
在CAS service端为了把属性传递到CAS client端,我们需要在deployerConfigContext.xml文件中配置以下信息:
<bean id="serviceRegistryDao" class="org.jasig.cas.services.InMemoryServiceRegistryDaoImpl"> <property name="registeredServices"> <list> <bean class="org.jasig.cas.services.RegisteredServiceImpl"> <property name="id" value="0" /> <property name="name" value="HTTP" /> <property name="description" value="Only Allows HTTP Urls" /> <property name="serviceId" value="http://**" /> <property name="evaluationOrder" value="10000001" /> <property name="allowedAttributes"> <list> <!-- weixin --> <value>openid</value> <value>nickname</value> <value>and so on</value> ...
3、在cas-server-support-pac4j项目的pom.xml增加必需的pac4j-* libraries
<dependency> <groupId>org.pac4j</groupId> <artifactId>pac4j-oauth</artifactId> <version>${pac4j.version}</version>
</dependency>
4、修改applicationContext.xml
在applicationContext.xml添加对应的oauth的clients。并把clients增加到对应的org.pac4j.core.client.Clients中,同样也是在applicationContext.xml
<bean id="clients" class="org.pac4j.core.client.Clients"> <property name="callbackUrl" value="https://login.nmall.com/cas/login" /> <property name="clients"> <list> <ref bean="weiXin" /> <ref bean="qq" /> </list> </property>
</bean>
<bean id="weiXin" class="org.jasig.cas.support.pac4j.plugin.weixin.WeiXinClient"> <property name="key" value="yourkey" /> <property name="secret" value="yousecret" />
</bean>
<bean id="qq" class="org.jasig.cas.support.pac4j.plugin.qq.QqClient"> <property name="key" value="yourkey" /> <property name="secret" value="yousecret" />
</bean>
5、修改login-webflow.xml,增加oauth用户验证逻辑
把处理oauth的client action添加到webflow中在login-webflow.xml中,这clientAction添加在webflow的最前面.它的任务是微信oauth用户验证的callback的调用.
<action-state id="clientAction"> <evaluate expression="clientAction" /> <transition on="success" to="sendTicketGrantingTicket" /> <transition on="error" to="ticketGrantingTicketCheck" /> <transition on="stop" to="stopWebflow" />
</action-state>
<view-state id="stopWebflow" />
clientAction这个bean必须定义在cas-servlet.xml,并且需要注入clients.
<bean id="clientAction" class="org.jasig.cas.support.pac4j.web.flow.ClientAction"> <constructor-arg index="0" ref="centralAuthenticationService"/> <constructor-arg index="1" ref="clients"/>
</bean
clientAction需要用到的centralAuthenticationService这个bean已经之前的用户验证已经配置好了。
6、增加用户验证的handler与数据入口.
<bean id="authenticationManager" class="org.jasig.cas.authentication.PolicyBasedAuthenticationManager"> <constructor-arg> <map> <entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver" /> <entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" /> <entry key-ref="customerDBAuthHandler" value-ref="customerDBPrincipalResolver" /> </map> </constructor-arg> <property name="authenticationMetaDataPopulators"> <util:list> <bean class="org.jasig.cas.support.pac4j.authentication.ClientAuthenticationMetaDataPopulator" /> </util:list> </property> <property name="authenticationPolicy"> <bean class="org.jasig.cas.authentication.AnyAuthenticationPolicy" /> </property>
</bean>
<bean id="primaryAuthenticationHandler" class="org.jasig.cas.support.pac4j.authentication.handler.support.ClientAuthenticationHandler"> <constructor-arg index="0" ref="clients"/>
</bean>
<bean id="primaryPrincipalResolver" class="org.jasig.cas.support.pac4j.plugin.common.OauthPersonDirectoryPrincipalResolver" / >
这个OauthPersonDirectoryPrincipalResolver类是需要自己重写的,此类用于oauth微信返回的用户信息处理返回给cas client端。
package org.jasig.cas.support.pac4j.plugin.common; import java.util.Map; import org.jasig.cas.authentication.Credential;
import org.jasig.cas.authentication.principal.Principal;
import org.jasig.cas.authentication.principal.PrincipalResolver;
import org.jasig.cas.authentication.principal.SimplePrincipal;
import org.jasig.cas.support.pac4j.authentication.principal.ClientCredential;
import org.pac4j.core.profile.UserProfile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class OauthPersonDirectoryPrincipalResolver implements PrincipalResolver { /** Log instance. */ protected final Logger logger = LoggerFactory.getLogger(this.getClass()); private boolean returnNullIfNoAttributes = false; public void setReturnNullIfNoAttributes(final boolean returnNullIfNoAttributes) { this.returnNullIfNoAttributes = returnNullIfNoAttributes; } @Override public Principal resolve(Credential credential) { logger.debug("Attempting to resolve a principal..."); if (credential instanceof ClientCredential){ // do nothing } else { throw new RuntimeException("用户数据转换异常!"); } ClientCredential oauthCredential = (ClientCredential) credential; String principalId = oauthCredential.getUserProfile().getId(); if (principalId == null) { logger.debug("Got null for extracted principal ID; returning null."); return null; } logger.debug("Creating SimplePrincipal for [{}]", principalId); UserProfile userProfile = oauthCredential.getUserProfile(); final Map<String, Object> attributes = userProfile.getAttributes(); if (attributes == null & !this.returnNullIfNoAttributes) { return new SimplePrincipal(principalId); } if (attributes == null) { return null; } return new SimplePrincipal(principalId, attributes); } @Override public boolean supports(Credential credential) { return true; } }
7、最后为了添加用户验证在远程调用
最后为了添加用户验证在远程调用,也就是微信。必须添加以下链接到登录页面casLoginView.jsp(ClientNameUrl这个属性是被ClientAction自动创建).也就是你自定义的Client类名加上Url.如我创建的类为WeixinClient则对应的link名为WeixinClientUrl。
<a href="${WeiXinClientUrl}">Authenticate with Wechat</a> <br />
<br />
<a href="${QqClientUrl}">Authenticate with QQ</a><br />
8、修改cas-server-support-pac4j(重点)
以上都是准备工作,下面才是真正的工作。因为在pac4j-oauth-1.4.1.jar这个包里面没有对weixin的相关支持所有只有自己手动修改了。以下的类是基于pac4j-oauth-1.4.1.jar包org.pac4j.oauth.client包中用于oauth 2.0协议与CAS集成类的对应与的。项目结构如下图所示:
1)用于处理CAS与微信的OAUTH通信。
package org.jasig.cas.support.pac4j.plugin.weixin; import org.pac4j.core.client.BaseClient;
import org.pac4j.core.context.WebContext;
import org.pac4j.core.exception.HttpCommunicationException;
import org.pac4j.oauth.client.BaseOAuth20Client;
import org.pac4j.oauth.credentials.OAuthCredentials;
import org.pac4j.oauth.profile.JsonHelper;
import org.scribe.model.OAuthConfig;
import org.scribe.model.ProxyOAuthRequest;
import org.scribe.model.Response;
import org.scribe.model.SignatureType;
import org.scribe.model.Token;
import org.springframework.beans.factory.annotation.Autowired; import com.fasterxml.jackson.databind.JsonNode;
import com.lm.b2c.core.enumerate.LoginKeyType;
import com.lm.b2c.core.lang.Pair;
import com.lm.b2c.user.manager.customer.api.CustomerManager;
import com.lm.b2c.user.manager.customer.vo.CombinedAccountVO; /** * 此类用于处理CAS与微信的OAUTH通信 * @author b2c021 * */
public class WeiXinClient extends BaseOAuth20Client<WeiXinProfile> { private final static WeiXinAttributesDefinition WEI_XIN_ATTRIBUTES = new WeiXinAttributesDefinition(); @Autowired private CustomerManager customerManager; public WeiXinClient(){} public WeiXinClient(final String key, final String secret){ setKey(key); setSecret(secret); } @Override protected BaseClient<OAuthCredentials, WeiXinProfile> newClient() { // TODO WeiXinClient newClient = new WeiXinClient(); return newClient; } @Override protected void internalInit() { // TODO super.internalInit(); WeiXinApi20 api = new WeiXinApi20(); this.service = new WeiXinOAuth20ServiceImpl(api, new OAuthConfig(this.key, this.secret, this.callbackUrl,SignatureType.Header, null, null), this.connectTimeout, this.readTimeout, this.proxyHost,this.proxyPort); } @Override protected String getProfileUrl() { // TODO Auto-generated method stub // eg.google2Client:return "https://www.googleapis.com/oauth2/v2/userinfo"; return "https://api.weixin.qq.com/sns/userinfo"; } @Override protected WeiXinProfile extractUserProfile(String body) { WeiXinProfile weiXinProfile = new WeiXinProfile(); final JsonNode json = JsonHelper.getFirstNode(body); if (null != json) { for(final String attribute : WEI_XIN_ATTRIBUTES.getPrincipalAttributes()){ weiXinProfile.addAttribute(attribute, JsonHelper.get(json, attribute)); } /** 绑定账号到系统 */ String openId = (String) weiXinProfile.getAttributes().get("openid"); String nickName = (String) weiXinProfile.getAttributes().get("nickname"); CombinedAccountVO combinedAccount = generateAccount(openId, LoginKeyType.WECHAT, nickName); Pair<Long,String> suidAndLoginName = customerManager.bindAccount(combinedAccount); weiXinProfile.addAttribute("suid", suidAndLoginName.getFirst()); weiXinProfile.setId(suidAndLoginName.getSecond()); } return weiXinProfile; } /** * 需求state元素 */ @Override protected boolean requiresStateParameter() { return false; } @Override // Cancelled 取消 protected boolean hasBeenCancelled(WebContext context) { return false; } @Override protected String sendRequestForData(final Token accessToken, final String dataUrl) { logger.debug("accessToken : {} / dataUrl : {}", accessToken, dataUrl); final long t0 = System.currentTimeMillis(); final ProxyOAuthRequest request = createProxyRequest(dataUrl); this.service.signRequest(accessToken, request); final Response response = request.send(); final int code = response.getCode(); final String body = response.getBody(); final long t1 = System.currentTimeMillis(); logger.debug("Request took : " + (t1 - t0) + " ms for : " + dataUrl); logger.debug("response code : {} / response body : {}", code, body); if (code != 200) { logger.error("Failed to get data, code : " + code + " / body : " + body); throw new HttpCommunicationException(code, body); } return body; } private CombinedAccountVO generateAccount(String openId,LoginKeyType keyType, String nickName){ CombinedAccountVO vo = new CombinedAccountVO(); vo.setLoginKey(openId); vo.setKeyType(keyType); vo.setNickName(nickName); return vo; }
}
2)、用于定义获取微信返回的CODE与ACCESSTOKEN
package org.jasig.cas.support.pac4j.plugin.weixin; import org.scribe.builder.api.DefaultApi20;
import org.scribe.extractors.AccessTokenExtractor;
import org.scribe.extractors.JsonTokenExtractor;
import org.scribe.model.OAuthConfig;
import org.scribe.model.Verb;
import org.scribe.utils.OAuthEncoder; /** * 用于定义获取微信返回的CODE与ACCESS_TOKEN * @author b2c021 * */
public class WeiXinApi20 extends DefaultApi20 { private static final String WEIXIN_AUTHORIZE_URL = "https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_login#wechat_redirect"; @Override public AccessTokenExtractor getAccessTokenExtractor() { return new JsonTokenExtractor(); } @Override public Verb getAccessTokenVerb() { return Verb.POST; } @Override public String getAccessTokenEndpoint() { return "https://api.weixin.qq.com/sns/oauth2/access_token"; } @Override public String getAuthorizationUrl(OAuthConfig config) { return String.format(WEIXIN_AUTHORIZE_URL, config.getApiKey(), OAuthEncoder.encode(config.getCallback())); } }
3)、用于接收微信返回的用户信息
package org.jasig.cas.support.pac4j.plugin.weixin; import org.pac4j.core.profile.converter.Converters;
import org.pac4j.oauth.profile.OAuthAttributesDefinition; /** * 用于接收微信返回的用户信息 * @author b2c021 * */
public class WeiXinAttributesDefinition extends OAuthAttributesDefinition { public static final String OPEN_ID = "openid"; public static final String NICK_NAME = "nickname"; /** 用户性别,1为男性,2为女性 */ public static final String SEX = "sex"; public static final String COUNTRY = "country"; public static final String PROVINCE = "province"; public static final String CITY = "city"; public static final String HEAD_IMG_URL = "headimgurl"; public static final String PRIVILEGE = "privilege"; public static final String UNION_ID = "unionid"; // appended public static final String APP_NAME = "appName"; public static final String SUID = "suid"; public WeiXinAttributesDefinition(){ addAttribute(OPEN_ID, Converters.stringConverter); addAttribute(NICK_NAME, Converters.stringConverter); addAttribute(SEX, Converters.integerConverter); addAttribute(COUNTRY, Converters.stringConverter); addAttribute(PROVINCE, Converters.stringConverter); addAttribute(CITY, Converters.stringConverter); addAttribute(HEAD_IMG_URL, Converters.stringConverter); addAttribute(UNION_ID, Converters.stringConverter); addAttribute(APP_NAME, Converters.stringConverter); addAttribute(SUID, Converters.longConverter); }
}
4)、用于获取微信返回的ACCESS_TOKEN
package org.jasig.cas.support.pac4j.plugin.weixin; import java.util.regex.Matcher;
import java.util.regex.Pattern; import org.scribe.exceptions.OAuthException;
import org.scribe.model.Token;
import org.scribe.utils.Preconditions; /** * 用于获取微信返回的ACCESS_TOKEN * @author b2c021 * */
public class WeiXinJsonTokenExtractor { private Pattern accessTokenPattern = Pattern.compile("\"access_token\":\\s*\"(\\S*?)\""); public Token extract(String response){ Preconditions.checkEmptyString(response, "Cannot extract a token from a null or empty String"); Matcher matcher = accessTokenPattern.matcher(response); if(matcher.find()){ return new Token(matcher.group(1), "", response); } else{ throw new OAuthException("Cannot extract an acces token. Response was: " + response); } } }
5)、用于添加获取ACCESS_TOKEN与用户信息添加参数并请求微信
package org.jasig.cas.support.pac4j.plugin.weixin; import java.util.regex.Matcher;
import java.util.regex.Pattern; import org.scribe.builder.api.DefaultApi20;
import org.scribe.exceptions.OAuthException;
import org.scribe.model.OAuthConfig;
import org.scribe.model.OAuthConstants;
import org.scribe.model.OAuthRequest;
import org.scribe.model.ProxyOAuthRequest;
import org.scribe.model.Response;
import org.scribe.model.Token;
import org.scribe.model.Verifier;
import org.scribe.oauth.ProxyOAuth20ServiceImpl; /** * 用于添加获取ACCESS_TOKEN与用户信息添加参数并请求微信 * @author b2c021 * */
public class WeiXinOAuth20ServiceImpl extends ProxyOAuth20ServiceImpl { private static Pattern openIdPattern = Pattern.compile("\"openid\":\\s*\"(\\S*?)\""); public WeiXinOAuth20ServiceImpl(DefaultApi20 api, OAuthConfig config, int connectTimeout, int readTimeout, String proxyHost, int proxyPort) { super(api, config, connectTimeout, readTimeout, proxyHost, proxyPort); } /** * 获取account_token的http请求参数添加 */ @Override public Token getAccessToken(final Token requestToken, final Verifier verifier) { final OAuthRequest request = new ProxyOAuthRequest(this.api.getAccessTokenVerb(), this.api.getAccessTokenEndpoint(), this.connectTimeout, this.readTimeout, this.proxyHost, this.proxyPort); request.addBodyParameter("appid", this.config.getApiKey()); request.addBodyParameter("secret", this.config.getApiSecret()); request.addBodyParameter(OAuthConstants.CODE, verifier.getValue()); request.addBodyParameter(OAuthConstants.REDIRECT_URI, this.config.getCallback()); request.addBodyParameter("grant_type", "authorization_code"); final Response response = request.send(); return this.api.getAccessTokenExtractor().extract(response.getBody()); } @Override public void signRequest(final Token accessToken, final OAuthRequest request) { request.addQuerystringParameter(OAuthConstants.ACCESS_TOKEN, accessToken.getToken()); String response = accessToken.getRawResponse(); Matcher matcher = openIdPattern.matcher(response); if(matcher.find()){ request.addQuerystringParameter("openid", matcher.group(1)); } else{ throw new OAuthException("微信接口返回数据miss openid: " + response); } }
}
6)、用于添加返回用户信息
package org.jasig.cas.support.pac4j.plugin.weixin; import org.pac4j.core.profile.AttributesDefinition;
import org.pac4j.oauth.profile.OAuth20Profile; /** * 用于添加返回用户信息 * @author b2c021 * */
public class WeiXinProfile extends OAuth20Profile { private static final long serialVersionUID = -7969484323692570444L; @Override protected AttributesDefinition getAttributesDefinition() { return new WeiXinAttributesDefinition(); } }
引用地址:http://jasig.github.io/cas/4.0.x/integration/Delegate-Authentication.html
CAS SSO 4.0.x 集成OAuth(微信登陆示例)相关推荐
- CAS SSO 4.0.x 用户数据库验证
转载地址: CAS 4.0 单点登录教程 1.概述 单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一.SSO的定义是在多个应用系统中,用户只需要登录 ...
- CAS SSO 4.0.x 增加验证码
这一篇主要是讲解怎么在登录页上添加验证码功能,默认的登录页是只有用户名与密码功能.其他我觉得加验证码没什么用,因为现在我部门做的系统主要是放在内网里,外网是不能访问的.登录页的验证码主要是为了防止进账 ...
- 单点登录(shiro与Spring Security OAuth 2.0的集成)
单点登录(shiro与Spring Security OAuth 2.0的集成) shiro项目采用ruoyi,OAuth采用pig 若依:https://gitee.com/y_project/Ru ...
- OAuth 2.0设计(以微信登录为例)
在实际应用开发中,我们常常需要使用微信作为应用的登陆方式,不同于手Q登陆使用传统的ptlogin,微信登陆采用了OAuth 2.0的验证方式.本文将以微信登录为案例,具体分析介绍所采用的OAuth 2 ...
- CAS单点登录-第三方登录[QQ、微信、CSDN、GitHub](十四)
CAS单点登录-第三方登录[QQ.微信.CSDN.GitHub](十四) 注: 目前博文使用cas版本为5.1.5,由于5.2.x与5.1.x构建模式有差异,所以部分配置会有些偏差. 本章内容 简答介 ...
- CAS+SSO原理浅谈
http://www.cnblogs.com/yonsin/archive/2009/08/29/1556423.html SSO 是一个非常大的主题,我对这个主题有着深深的感受,自从广州 UserG ...
- 单点登录之CAS SSO从入门到精通(第三天)
开场白 各位新年好,上海的新年好冷,冷到我手发抖. 做好准备全身心投入到新的学习和工作中去了吗?因为今天开始的教程很"变态"啊,我们要完成下面几件事: 自定义CAS SSO登录界面 ...
- 移动共享开发(二)各平台SSO(免登录)配置 微信和微信朋友圈、QQ、QQ空间、新浪微博、腾讯微博、人人
SSO名词解释 : SSO英文全称Single Sign On,单点登录.SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统. 例如用户已经登录新浪微博客户端,使用SSO授权 ...
- CAS SSO 单点登录 【完整版】
什么是单点登录?什么是SSO? SSO就是单点登录!!! SSO即Single Sign On. 可是为什么我们要单点登录呢?为什么不能把所有的系统做成一个war包里呢? 道理很简单啊,如果这个银行这 ...
最新文章
- oracle创建数据库、表空间、用户并授权
- matconvnet中使用fastrcnn遇到的问题
- python中if __name__ == '__main__'功能的解析
- KMP算法 java版本
- Linux网络编程服务器模型选择之并发服务器(上)
- 一起学习C语言:初谈指针(三)
- python的numpy库结构_NumPy构成了数据科学领域中许多Python库的基础。
- PTA:6-8 数组元素的区间删除 (20 分)
- Hadoop面向行和面向列格式详解
- 艾宾浩斯遗忘曲线.pdf百日计划表.pdf考研时间计划表.pdf每日打卡.pdf每日复习计划表.pdf详细日计划.pdf月计划表.pdf周计划.pdf
- 宋佳乐和郭晓婷天津之眼观景照片
- Codeforces--1311A--Duff and Weight Lifting
- jxt解析上传的xls文件
- AR5B22网卡折腾记录
- 想要Linux上云?如何实现Linux工作流上云部署
- 网站增加百度收录最有效的方法!!!!!!
- 淘宝旺旺号转userid 和 uid 的接口方法
- Win10重装的方法?一键重装Win10的图文版教程
- unity3d:ugui 长按按钮
- 什么事长连接 短连接
热门文章
- 科技大佬们的编程水平怎么样?
- python搜索关键词自动提交_Python多线程采集百度相关搜索关键词工具带exe程序!...
- R语言gganimate包创建散点图动画图(gif)、transition_states函数根据分组变量创建动图、shadow_wake函数配置动画的渐变效果(gradual falloff)拖尾效应
- Oracle11g(RAC)补丁安装(31718723,31668908)
- 【imessage苹果群发家庭推】Development Provisioning Profile署名
- java web报表,jasperReport使用简介
- Debian 中 BCM43142 蓝牙无法使用问题
- idea:properties in parent definition are prohibited提示报错
- BZOJ 4296 PA2015 Mistrzostwa
- web前端(2)—— 前端技术介绍