[心缘地方]同学录
首页 | 功能说明 | 站长通知 | 最近更新 | 编码查看转换 | 代码下载 | 常见问题及讨论 | 《深入解析ASP核心技术》 | 王小鸭自动发工资条VBA版
登录系统:用户名: 密码: 如果要讨论问题,请先注册。

[整理]CAS 4.2.7版本,内网外网,自定义单点退出时调用的URL

上一篇:[备忘]javascript获取同域名的最顶层窗口
下一篇:[备忘]FastJson解析下划线字段问题 _id

添加日期:2017/7/21 10:02:08 快速返回   返回列表 阅读4322次
CAS 4.2.7版本,内网外网,自定义单点退出时调用的URL

前提:
shiro+CAS

问题:
阿里云服务器,服务器有外网ip和内网ip,
两台服务器之间,用外网ip是访问不了的。

如果在shiro配置里,子系统地址都写的外网地址,那么验证ticket这步是过不去的,因为访问不了cas服务器嘛。

解决:
(1)第一步,shiroFilter里有个loginUrl,这个直接写外网即可,没问题。
shiro发现没认证时,会返回302跳转,由浏览器跳转,所以应该是外网地址
如,http://www.aaa.com:28080/cas/login?service=http://www.bbb.com:38080/xxx/shiro-cas
在CAS服务器登录后,CAS服务器会用map记录ticket和service的关系,然后302跳转到www.bbb.com...,当然是带着ticket


(2)带着ticket过来后,shiro会调到CasRealm类,拿ticket到cas服务器去验证。
在配置文件里,你会配置一个bean,继承org.apache.shiro.cas.CasRealm类,
它有两个属性:
CasServerUrlPrefix
CasService
这两个就是验证ticket时,需要的参数

这个请求,是由你的子系统发起的,是两个服务器间的直接请求,所以,
CasServerUrlPrefix要写为内网ip,如http://192.168.1.111:28080/cas

但是CasService还是要写第一步那个,如http://www.bbb.com:38080/xxx/shiro-cas
因为cas服务器会根据ticket从map里取得servcie,然后跟你传过去的service做匹配,不匹配就会报那个ticket无效的异常。

(3)单点退出
执行cas/logout后,cas服务器会通知你访问过的所有service,进行退出。
问题来了,这个请求是cas服务器直接请求你的子系统的,属于系统间的请求,应该用内网ip的嘛~~
但是,上面搞了半天,也没告诉cas服务器,咱的子系统的内网地址的logoutURL啊~~

个人觉得,如果能在CasRealm那加个参数,如casServiceLogoutUrl,在验证ticket时,传给服务器,由服务器保存着,
单点退出时,拿这个url去调用,应该比较爽。

不过找了半天,貌似没这功能,自己搞也费劲。

搜了半天源代码,发现服务端的org.jasig.cas.logout.LogoutManagerImpl类的


 public List<LogoutRequest> performLogout(final TicketGrantingTicket ticket) {
        final Map<String, Service> services = ticket.getServices();
        final List<LogoutRequest> logoutRequests = new ArrayList<>();
        // if SLO is not disabled
        if (!this.singleLogoutCallbacksDisabled) {
            // through all services
            for (final Map.Entry<String, Service> entry : services.entrySet()) {
                // it's a SingleLogoutService, else ignore
                final Service service = entry.getValue();
                if (service instanceof SingleLogoutService) {
                    final LogoutRequest logoutRequest = handleLogoutForSloService((SingleLogoutService) service, entry.getKey());
                    if (logoutRequest != null) {
                        LOGGER.debug("Captured logout request [{}]", logoutRequest);
                        logoutRequests.add(logoutRequest);
                    }
                }
            }
        }

        return logoutRequests;
    }

private LogoutRequest handleLogoutForSloService(final SingleLogoutService singleLogoutService, final String ticketId) {
        if (!singleLogoutService.isLoggedOutAlready()) {

            final RegisteredService registeredService = servicesManager.findServiceBy(singleLogoutService);
            if (serviceSupportsSingleLogout(registeredService)) {

                final URL logoutUrl = determineLogoutUrl(registeredService, singleLogoutService);
                final DefaultLogoutRequest logoutRequest = new DefaultLogoutRequest(ticketId, singleLogoutService, logoutUrl);
                final LogoutType type = registeredService.getLogoutType() == null
                        ? LogoutType.BACK_CHANNEL : registeredService.getLogoutType();

                switch (type) {
                    case BACK_CHANNEL:
                        if (performBackChannelLogout(logoutRequest)) {
                            logoutRequest.setStatus(LogoutRequestStatus.SUCCESS);
                        } else {
                            logoutRequest.setStatus(LogoutRequestStatus.FAILURE);
                            LOGGER.warn("Logout message not sent to [{}]; Continuing processing...", singleLogoutService.getId());
                        }
                        break;
                    default:
                        logoutRequest.setStatus(LogoutRequestStatus.NOT_ATTEMPTED);
                        break;
                }
                return logoutRequest;
            }

        }
        return null;
    }

 /**
     * Determine logout url.
     *
     * @param registeredService the registered service
     * @param singleLogoutService the single logout service
     * @return the uRL
     */
    private URL determineLogoutUrl(final RegisteredService registeredService, final SingleLogoutService singleLogoutService) {
        try {
            URL logoutUrl = new URL(singleLogoutService.getOriginalUrl());
            final URL serviceLogoutUrl = registeredService.getLogoutUrl();

            if (serviceLogoutUrl != null) {
                LOGGER.debug("Logout request will be sent to [{}] for service [{}]",
                        serviceLogoutUrl, singleLogoutService);
                logoutUrl = serviceLogoutUrl;
            }
            return logoutUrl;
        } catch (final Exception e) {
            throw new IllegalArgumentException(e);
        }
    }



大概意思是,根据TGT找到所有的service,然后循环调用
从最后一段代码可以看出,如果registeredService.getLogoutUrl()有值的话,会用它的值去logout。
没有的话,就用的原始的url,也就是咱传的service。

registeredService = servicesManager.findServiceBy(singleLogoutService);

找了半天,找到
org.jasig.cas.services.DefaultServicesManagerImpl


public void load() {
        final ConcurrentHashMap<Long, RegisteredService> localServices =
                new ConcurrentHashMap<>();

        for (final RegisteredService r : this.serviceRegistryDao.load()) {
            LOGGER.debug("Adding registered service {}", r.getServiceId());
            localServices.put(r.getId(), r);
        }

        this.services = localServices;
        LOGGER.info("Loaded {} services from {}.", this.services.size(),
                this.serviceRegistryDao);

    }


就相当于一个Map,从serviceRegistryDao获取的一个Map,
serviceRegistryDao是在deployerConfigContext.xml中定义的,
默认是<alias name="jsonServiceRegistryDao" alias="serviceRegistryDao" />

cas.properties中有:
-----------
##
# JSON Service Registry
#
# Directory location where JSON service files may be found.
service.registry.config.location=classpath:services
--------------

所以,看了半天,其实就是WEB-INF\classes\services下的那几个json文件。
理论上,你可以给每个子系统写一个json文件。

解决方案:
复制一个HTTPSandIMAPS-10000001.json,改为xxxx.json,名字随便起吧。

------
{
  "@class" : "org.jasig.cas.services.RegexRegisteredService",
  "serviceId" : "^http://www\\.aaa\\.com:38080/xxxxGate.*",  ------>这行改成你的子系统地址,这是一个正则表达式
  "name" : "agentTicketGate", -->名字,随便写
  "id" : 10000003,  -->id注意别相同啊
  "description" : "agentTicketGate", -->描述,随便写
  "proxyPolicy" : {
    "@class" : "org.jasig.cas.services.RefuseRegisteredServiceProxyPolicy"
  },
  "evaluationOrder" : 2, -->匹配顺序吧,小的先匹配。HTTPSandIMAPS-10000001.json里写的是1000,也就是最后匹配
  "logoutUrl" : "http://www.bbb.com:38080/xxxGate/shiro-cas",  -->就是这个,哈哈
-----------------

特殊的工程都搞一个这样的文件,其他的都走HTTPSandIMAPS-10000001.json就行了。

效果就是,你用aaa.com访问子系统,但是单点退出时,CAS服务器,会调用bbb.com这个URL。

---------------------
别人写的,单点退出的代码解析,可以看看
https://my.oschina.net/thinwonton/blog/1475562
 

评论 COMMENTS
没有评论 No Comments.

添加评论 Add new comment.
昵称 Name:
评论内容 Comment:
验证码(不区分大小写)
Validation Code:
(not case sensitive)
看不清?点这里换一张!(Change it here!)
 
评论由管理员查看后才能显示。the comment will be showed after it is checked by admin.
CopyRight © 心缘地方 2005-2999. All Rights Reserved