如何在Spring的CAS服务属性中正确设置服务URL
当使用 Spring Security + CAS 时,我总是遇到发送到 CAS 的回调 URL(即服务属性)的小障碍。我查看了很多示例,例如 this 和 this 但它们都使用硬编码的 URL(甚至 Spring 的 CAS 文档)。典型的片段看起来像这样...
<bean id="serviceProperties" class="org.springframework.security.ui.cas.ServiceProperties">
<property name="service" value="http://localhost:8080/click/j_spring_cas_security_check" />
</bean>
首先,我不想硬编码服务器名称或端口,因为我希望这个 WAR 可以部署在任何地方,并且我不希望我的应用程序绑定到特定的 DNS 条目编译时间。其次,我不明白为什么 Spring 无法自动检测我的应用程序的上下文 和请求的 URL 以自动构建 URL。 该声明的第一部分仍然有效,但正如 Raghuram 在下面指出的那样 < a href="https://jira.springsource.org/browse/SEC-1374" rel="noreferrer">此链接,出于安全原因,我们不能信任来自客户端的 HTTP 主机标头。
理想情况下,我希望服务 URL 正是用户请求的(只要请求有效,例如 mycompany.com 的子域),这样它是无缝的,或者至少我只想指定一些相对于我的路径应用程序上下文根并使 Spring 动态确定服务 URL。类似以下内容...
<bean id="serviceProperties" class="org.springframework.security.ui.cas.ServiceProperties">
<property name="service" value="/my_cas_callback" />
</bean>
或者...
<bean id="serviceProperties" class="org.springframework.security.ui.cas.ServiceProperties">
<property name="service" value="${container.and.app.derived.value.here}" />
</bean>
这一切是否可能或容易,还是我错过了显而易见的事情?
When working with Spring Security + CAS I keep hitting a small road block with the callback URL that is sent to CAS, ie the service property. I've looked at a bunch of examples such as this and this but they all use hard coded URLs (even Spring's CAS docs). A typical snip looks something like this...
<bean id="serviceProperties" class="org.springframework.security.ui.cas.ServiceProperties">
<property name="service" value="http://localhost:8080/click/j_spring_cas_security_check" />
</bean>
First, I don't want to hard code the server name or the port since I want this WAR to be deployable anywhere and I don't want my application tied to a particular DNS entry at compile time. Second, I don't understand why Spring can't auto detect my application's context and the request's URL to automagically build the URL. The first part of that statement still stand but As Raghuram pointed out below with this link, we can't trust the HTTP Host Header from the client for security reasons.
Ideally I would like service URL to be exactly what the user requested (as long as the request is valid such as a sub domain of mycompany.com) so it is seamless or at the very least I would like to only specify some path relative my applications context root and have Spring determine the service URL on the fly. Something like the following...
<bean id="serviceProperties" class="org.springframework.security.ui.cas.ServiceProperties">
<property name="service" value="/my_cas_callback" />
</bean>
OR...
<bean id="serviceProperties" class="org.springframework.security.ui.cas.ServiceProperties">
<property name="service" value="${container.and.app.derived.value.here}" />
</bean>
Is any of this possible or easy or have I missed the obvious?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我知道这有点旧,但我只需要解决这个问题,并且在新堆栈中找不到任何东西。
我们有多个环境共享相同的 CAS 服务(例如 dev、qa、uat 和本地开发环境);我们能够从多个 url 访问每个环境(通过反向代理上的客户端 Web 服务器并直接到达后端服务器本身)。这意味着指定单个 url 是很困难的。也许有一种方法可以做到这一点,但可以使用动态
ServiceProperties.getService()
。我可能会添加某种服务器后缀检查,以确保网址在某些时候不会被劫持。以下是我为使基本 CAS 流程正常工作而所做的工作,无论用于访问受保护资源的 URL 是什么...
CasAuthenticationFilter
。CasAuthenticationProvider
。ServiceProperties
上的setAuthenticateAllArtifacts(true)
。这是我的 spring 配置 bean 的长形式:
只是通常的 spring 配置 bean。
一些外部化的配置参数。
上面的关键是
setAuthenticateAllArtifacts(true)
调用。这将使服务票证验证器使用 AuthenticationDetailsSource 实现,而不是硬编码的ServiceProperties.getService()
调用标准票证验证器。
现有 UserDetailsService
标准身份验证 的标准挂钩此处的关键
是
dynamicServiceResolver()
设置。通过
makeDynamicUrlFromRequest()
方法动态创建服务 URL。该位在票证验证时使用。当 CAS 想要重定向到登录屏幕时,这部分使用相同的动态 url 创建器。
这就是你所做的一切。我只传入 ServiceProperties 来保存我们配置的服务的 URI。我们在后端使用 HATEAOS 并有一个类似的实现:
编辑:这是我对有效服务器后缀列表所做的操作。
I know this is a bit old but I just had to solve this very problem and couldn't really find anything in the newer stacks.
We have multiple environments sharing the same CAS service (think dev, qa, uat and local development environments); we have the ability to hit each environment from more than one url (via the client side web server over a reverse proxy and directly to the back-end server itself). This means that specifying a single url is difficult at best. Maybe there's a way to do this but being able to use a dynamic
ServiceProperties.getService()
. I'll probably add some kind of server suffix check to ensure that the url isn't hijacked at some point.Here's what I did to get the basic CAS flow working regardless of the URL used to access the secured resource...
CasAuthenticationFilter
.CasAuthenticationProvider
.setAuthenticateAllArtifacts(true)
on theServiceProperties
.Here's the long form of my spring configuration bean:
Just the usual spring configuration bean.
Some externalized configuration parameters.
The key thing above is the
setAuthenticateAllArtifacts(true)
call. This will make the service ticket validator use theAuthenticationDetailsSource
implementation rather than a hard-codedServiceProperties.getService()
callStandard ticket validator..
Standard hook to an existing UserDetailsService
Standard authentication provider
Key here is the
dynamicServiceResolver()
setting..Dynamically creates the service url from the
makeDynamicUrlFromRequest()
method. This bit is used upon ticket validation.This part uses the same dynamic url creator when CAS wants to redirect to the login screen.
This is whatever you make of it. I only passed in the ServiceProperties to hold the URI of the service that we're configured for. We use HATEAOS on the back-side and have an implementation like:
Edit: here's what I did for the list of valid server suffixes..
在 Spring 2.6.5 spring 中,您可以扩展 org.springframework.security.ui.cas.ServiceProperties
在 spring 3 中,该方法是最终的,您可以通过子类化 CasAuthenticationProvider 和 CasEntryPoint 来解决此问题,然后与您自己的 ServiceProperties 版本一起使用并覆盖getService() 方法具有更动态的实现。
您可以使用主机标头来计算所需的域,并通过验证仅使用您控制下的域/子域来使其更加安全。然后附加一些可配置的值。
当然,您将面临实施不安全的风险……所以要小心。
它最终可能看起来像:
In Spring 2.6.5 spring you could extend org.springframework.security.ui.cas.ServiceProperties
In spring 3 the method is final you could get around this by subclassing the CasAuthenticationProvider and CasEntryPoint and then use with your own version of ServiceProperties and override the getService() method with a more dynamic implementation.
You could use the host header to calculate the the required domain and make it more secure by validating that only domains/subdomains under your control are used. Then append to this some configurable value.
Of course you would be at risk that your implementation was insecure though... so be careful.
It could end up looking like:
使用 Maven,添加属性占位符,并在构建过程中配置它
use maven, add a property placeholder, and configure it in your build process
我尝试按照 Pablojim 的建议对 CasAuthenticationProvider 进行子类化,但解决方案非常简单!使用 Spring 表达式语言 (SPEL),您可以动态获取 url。
示例:
<属性名称=“服务”
value="https://#{T(java.net.InetAddress).getLocalHost().getHostName()}:${application.port}${cas.service}/login/cascheck"/>
I tried to subclass CasAuthenticationProvider as Pablojim suggest, but solution is very easier! with Spring Expression Language (SPEL) you can obtain the url dinamically.
Example:
<property name="service"
value="https://#{T(java.net.InetAddress).getLocalHost().getHostName()}:${application.port}${cas.service}/login/cascheck"/>
我自己没有尝试过,但 Spring Security 似乎有一个解决方案
SavedRequestAwareAuthenticationSuccessHandler
显示在 鲍勃的博客。I have not tried this myself, but it seems Spring Security has a solution to this with the
SavedRequestAwareAuthenticationSuccessHandler
shown in the update of Bob's blog.