CAS SSO 4.0.x 集成OAuth(微信登陆示例)
发布日期:2021-06-29 12:51:46 浏览次数:2 分类:技术文章

本文共 15824 字,大约阅读时间需要 52 分钟。

由于公司需要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.

org.jasig.cas
cas-server-support-pac4j
${cas.version}

2、配置传递属性

在CAS service端为了把属性传递到CAS client端,我们需要在deployerConfigContext.xml文件中配置以下信息:

openid
nickname
and so on
...

3、在cas-server-support-pac4j项目的pom.xml增加必需的pac4j-* libraries

org.pac4j
pac4j-oauth
${pac4j.version}

4、修改applicationContext.xml

在applicationContext.xml添加对应的oauth的clients。并把clients增加到对应的org.pac4j.core.client.Clients中,同样也是在applicationContext.xml

5、修改login-webflow.xml,增加oauth用户验证逻辑

把处理oauth的client action添加到webflow中在login-webflow.xml中,这clientAction添加在webflow的最前面.它的任务是微信oauth用户验证的callback的调用.

clientAction这个bean必须定义在cas-servlet.xml,并且需要注入clients.

clientAction需要用到的centralAuthenticationService这个bean已经之前的用户验证已经配置好了。

6、增加用户验证的handler与数据入口.

这个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
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。

Authenticate with Wechat 
Authenticate with QQ

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
{
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
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
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(); } }

引用地址:

转载地址:https://carlzone.blog.csdn.net/article/details/51729962 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:1、Spring MVC 介绍
下一篇:CAS SSO 4.0.x 增加验证码

发表评论

最新留言

路过,博主的博客真漂亮。。
[***.116.15.85]2024年04月06日 20时04分56秒