配置 Spring Security 3.x 以具有多个入口点
我一直在使用 Spring Security 3.x 来处理我的项目的用户身份验证,到目前为止,它运行得非常完美。
我最近收到了一个新项目的要求。在此项目中,需要两组用户身份验证:一组根据 LDAP 对员工进行身份验证,另一组根据数据库对客户进行身份验证。我对如何在 Spring Security 中配置它有点困惑。
我最初的想法是创建一个包含以下字段的登录屏幕:-
- 单选按钮字段 - 供用户选择他们是员工还是客户。
j_username
用户字段。j_password
密码字段。
如果用户选择“employee”,那么我希望 Spring Security 根据 LDAP 对他们进行身份验证,否则凭证将根据数据库进行身份验证。但是,问题是表单将提交到 /j_spring_security_check
并且我无法将单选按钮字段发送到我实现的自定义身份验证提供程序。我最初的想法是我可能需要两个表单提交 URL,而不是依赖默认的 /j_spring_security_check
。每个 URL 将由不同的身份验证提供程序处理,但我不确定如何在 Spring Security 中配置它。
我知道在 Spring Security 中,我可以配置回退身份验证,例如,如果 LDAP 身份验证失败,那么它将回退到数据库身份验证,但这不是我在这个新项目中的目标。
有人可以分享一下我应该如何在 Spring Security 3.x 中配置它吗?
谢谢。
更新 - 01-28-2011 - @EasyAngel 的技术
我正在尝试执行以下操作:-
- 员工表单登录提交到
/j_spring_security_check_for_employee
- 客户表单登录提交到
/j_spring_security_check_for_customer
我想要 2 个不同表单登录的原因是允许我根据用户以不同方式处理身份验证,而不是进行后备身份验证。就我而言,员工和客户可能具有相同的用户 ID。
我采纳了@EasyAngel的想法,但必须替换一些已弃用的类。我当前面临的问题是过滤器进程 URL 似乎都没有在 Spring Security 中注册,因为我不断收到错误 404:SRVE0190E:找不到文件:/j_spring_security_check_for_employee
。我的直觉是 springSecurityFilterChain bean 没有正确连接,因此我的自定义过滤器根本没有被使用。
顺便说一下,我使用的是 WebSphere,并且在服务器中设置了 com.ibm.ws.webcontainer.invokefilterscompatibility=true 属性。我可以毫无问题地点击默认的 /j_spring_security_check
。
这是我完整的安全配置:-
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:sec="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
<sec:http auto-config="true">
<sec:form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1" default-target-url="/welcome.jsp"
always-use-default-target="true" />
<sec:logout logout-success-url="/login.jsp" />
<sec:intercept-url pattern="/employee/**" access="ROLE_EMPLOYEE" />
<sec:intercept-url pattern="/customer/**" access="ROLE_CUSTOMER" />
<sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
</sec:http>
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/**" filters="authenticationProcessingFilterForEmployee, authenticationProcessingFilterForCustomer" />
</sec:filter-chain-map>
</bean>
<bean id="authenticationProcessingFilterForEmployee" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManagerForEmployee" />
<property name="filterProcessesUrl" value="/j_spring_security_check_for_employee" />
</bean>
<bean id="authenticationProcessingFilterForCustomer" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManagerForCustomer" />
<property name="filterProcessesUrl" value="/j_spring_security_check_for_customer" />
</bean>
<bean id="authenticationManagerForEmployee" class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<ref bean="employeeCustomAuthenticationProvider" />
</list>
</property>
</bean>
<bean id="authenticationManagerForCustomer" class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<ref bean="customerCustomAuthenticationProvider" />
</list>
</property>
</bean>
<bean id="employeeCustomAuthenticationProvider" class="ss.EmployeeCustomAuthenticationProvider">
<property name="userDetailsService">
<bean class="ss.EmployeeUserDetailsService"/>
</property>
</bean>
<bean id="customerCustomAuthenticationProvider" class="ss.CustomerCustomAuthenticationProvider">
<property name="userDetailsService">
<bean class="ss.CustomerUserDetailsService"/>
</property>
</bean>
<sec:authentication-manager>
<sec:authentication-provider ref="employeeCustomAuthenticationProvider" />
<sec:authentication-provider ref="customerCustomAuthenticationProvider" />
</sec:authentication-manager>
</beans>
我在这里开始赏金,因为我似乎已经好几天无法让它工作了......这个词就是沮丧。我希望有人能指出问题,或者您是否可以向我展示更好或更干净的方法来处理此问题(在代码中)。
我正在使用 Spring Security 3.x。
谢谢。
更新 01-29-2011 - @Ritesh 的技术
好的,我设法让 @Ritesh 的方法非常接近我想要的效果。我有一个单选按钮,允许用户选择他们是客户还是员工。看起来这种方法运行得相当好,但有一个问题......
- 如果员工使用正确的凭据登录,他们就被允许......按预期工作。
- 如果员工使用错误的凭据登录,他们将不被允许...按预期工作。
- 如果客户使用正确的凭据登录,他们就可以...按预期工作。
- 如果客户使用错误的凭据登录,身份验证将退回到员工身份验证...不起作用。这是有风险的,因为如果我选择客户身份验证,并打入员工凭证,它也会允许用户进入,而这不是我想要的。
<sec:http auto-config="false" entry-point-ref="loginUrlAuthenticationEntryPoint">
<sec:logout logout-success-url="/login.jsp"/>
<sec:intercept-url pattern="/employee/**" access="ROLE_EMPLOYEE"/>
<sec:intercept-url pattern="/customer/**" access="ROLE_CUSTOMER"/>
<sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<sec:custom-filter position="FORM_LOGIN_FILTER" ref="myAuthenticationFilter"/>
</sec:http>
<bean id="myAuthenticationFilter" class="ss.MyAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureHandler" ref="failureHandler"/>
<property name="authenticationSuccessHandler" ref="successHandler"/>
</bean>
<bean id="loginUrlAuthenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/login.jsp"/>
</bean>
<bean id="successHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/welcome.jsp"/>
<property name="alwaysUseDefaultTargetUrl" value="true"/>
</bean>
<bean id="failureHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/login.jsp?login_error=1"/>
</bean>
<bean id="employeeCustomAuthenticationProvider" class="ss.EmployeeCustomAuthenticationProvider">
<property name="userDetailsService">
<bean class="ss.EmployeeUserDetailsService"/>
</property>
</bean>
<bean id="customerCustomAuthenticationProvider" class="ss.CustomerCustomAuthenticationProvider">
<property name="userDetailsService">
<bean class="ss.CustomerUserDetailsService"/>
</property>
</bean>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider ref="customerCustomAuthenticationProvider"/>
<sec:authentication-provider ref="employeeCustomAuthenticationProvider"/>
</sec:authentication-manager>
</beans>
这是我更新的配置。我需要做一些非常小的调整来防止身份验证回落,但我现在似乎无法弄清楚。
谢谢。
更新 - @Ritesh 技术的解决方案
好的,我想我已经解决了这里的问题。我没有让 EmployeeCustomAuthenticationProvider
依赖于默认的 UsernamePasswordAuthenticationToken
,而是为它创建了 EmployeeUsernamePasswordAuthenticationToken
,就像我创建的 CustomerUsernamePasswordAuthenticationToken
一样。代码> 为 CustomerCustomAuthenticationProvider
。然后,这些提供程序将覆盖 supports()
:-
CustomerCustomAuthenticationProvider 类
@Override
public boolean supports(Class<? extends Object> authentication) {
return (CustomerUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
EmployeeCustomAuthenticationProvider 类
@Override
public boolean supports(Class<? extends Object> authentication) {
return (EmployeeUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
MyAuthenticationFilter 类
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
...
UsernamePasswordAuthenticationToken authRequest = null;
if ("customer".equals(request.getParameter("radioAuthenticationType"))) {
authRequest = new CustomerUsernamePasswordAuthenticationToken(username, password);
}
else {
authRequest = new EmployeeUsernamePasswordAuthenticationToken(username, password);
}
setDetails(request, authRequest);
return super.getAuthenticationManager().authenticate(authRequest);
}
... 和 WALAA !经过几天的挫败,现在完美运行了!
希望这篇文章能够帮助那些和我在这里做同样事情的人。
I have been using Spring Security 3.x for handling user authentication for my projects, and so far, it has worked flawlessly.
I recently received the requirements for a new project. In this project, it requires 2 sets of user authentication: one to authenticate employees against LDAP, and another to authenticate customer against database. I'm a little stumped on how to configure that in Spring Security.
My initial idea was to create a login screen that has the following fields:-
- radio button field - for users to choose whether they are employees or customers.
j_username
user field.j_password
password field.
If the user selects "employee", then I want Spring Security to authenticate them against LDAP, otherwise the credential will be authenticated against database. However, the problem is the form will be submitted to /j_spring_security_check
and there's no way for me to send the radio button field to my implemented custom authentication provider. My initial thought is I probably need two form submission URLs rather than relying on the default /j_spring_security_check
. Each URL will be handled by different authentication providers, but I'm not sure how to configure that in Spring Security.
I know in Spring Security, I can configure fall back authentication, for example if LDAP authentication fails, then it will fall back to database authentication, but this is not what I'm shooting for in this new project.
Can someone share how exactly I should configure this in Spring Security 3.x?
Thank you.
UPDATE - 01-28-2011 - @EasyAngel's technique
I'm trying to do the following:-
- Employee form login submits to
/j_spring_security_check_for_employee
- Customer form login submits to
/j_spring_security_check_for_customer
The reason I want 2 different form logins is to allow me to handle the authentication differently based on the user, instead of doing a fall-back authentication. It is possible that employee and customer have same user ID, in my case.
I incorporated @EasyAngel's idea, but have to replace some deprecated classes. The problem I'm currently facing is neither filter processes URLS seem registered in Spring Security because I keep getting Error 404: SRVE0190E: File not found: /j_spring_security_check_for_employee
. My gut feeling is the springSecurityFilterChain
bean is not wired properly, thus my custom filters are not used at all.
By the way, I'm using WebSphere and I do have com.ibm.ws.webcontainer.invokefilterscompatibility=true
property set in the server. I'm able to hit the default /j_spring_security_check
without problem.
Here's my complete security configuration:-
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:sec="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
<sec:http auto-config="true">
<sec:form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1" default-target-url="/welcome.jsp"
always-use-default-target="true" />
<sec:logout logout-success-url="/login.jsp" />
<sec:intercept-url pattern="/employee/**" access="ROLE_EMPLOYEE" />
<sec:intercept-url pattern="/customer/**" access="ROLE_CUSTOMER" />
<sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
</sec:http>
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/**" filters="authenticationProcessingFilterForEmployee, authenticationProcessingFilterForCustomer" />
</sec:filter-chain-map>
</bean>
<bean id="authenticationProcessingFilterForEmployee" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManagerForEmployee" />
<property name="filterProcessesUrl" value="/j_spring_security_check_for_employee" />
</bean>
<bean id="authenticationProcessingFilterForCustomer" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManagerForCustomer" />
<property name="filterProcessesUrl" value="/j_spring_security_check_for_customer" />
</bean>
<bean id="authenticationManagerForEmployee" class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<ref bean="employeeCustomAuthenticationProvider" />
</list>
</property>
</bean>
<bean id="authenticationManagerForCustomer" class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<ref bean="customerCustomAuthenticationProvider" />
</list>
</property>
</bean>
<bean id="employeeCustomAuthenticationProvider" class="ss.EmployeeCustomAuthenticationProvider">
<property name="userDetailsService">
<bean class="ss.EmployeeUserDetailsService"/>
</property>
</bean>
<bean id="customerCustomAuthenticationProvider" class="ss.CustomerCustomAuthenticationProvider">
<property name="userDetailsService">
<bean class="ss.CustomerUserDetailsService"/>
</property>
</bean>
<sec:authentication-manager>
<sec:authentication-provider ref="employeeCustomAuthenticationProvider" />
<sec:authentication-provider ref="customerCustomAuthenticationProvider" />
</sec:authentication-manager>
</beans>
I'm starting a bounty here because I can't seem to get this working for several days already... frustration is the word. I'm hoping someone will point out the problem(s), or if you can show me a better or cleaner way to handle this (in code).
I'm using Spring Security 3.x.
Thank you.
UPDATE 01-29-2011 - @Ritesh's technique
Okay, I managed to get @Ritesh's approach to work very closely to what I wanted. I have the radiobutton that allows user to select whether they are customer or employee. It seems like this approach is working fairly well, with one problem...
- If employee logs in with right credential, they are allowed in... WORK AS EXPECTED.
- If employee logs in with wrong credential, they are not allowed in... WORK AS EXPECTED.
- If customer logs in with right credential, they are allowed in... WORK AS EXPECTED.
- If customer logs in with wrong credential, the authentication falls back to employee authentication... DOESN'T WORK. This is risky because if I select customer authentication, and punch it the employee credential, it will allow the user in too and this is not what I want.
<sec:http auto-config="false" entry-point-ref="loginUrlAuthenticationEntryPoint">
<sec:logout logout-success-url="/login.jsp"/>
<sec:intercept-url pattern="/employee/**" access="ROLE_EMPLOYEE"/>
<sec:intercept-url pattern="/customer/**" access="ROLE_CUSTOMER"/>
<sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<sec:custom-filter position="FORM_LOGIN_FILTER" ref="myAuthenticationFilter"/>
</sec:http>
<bean id="myAuthenticationFilter" class="ss.MyAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureHandler" ref="failureHandler"/>
<property name="authenticationSuccessHandler" ref="successHandler"/>
</bean>
<bean id="loginUrlAuthenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/login.jsp"/>
</bean>
<bean id="successHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/welcome.jsp"/>
<property name="alwaysUseDefaultTargetUrl" value="true"/>
</bean>
<bean id="failureHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/login.jsp?login_error=1"/>
</bean>
<bean id="employeeCustomAuthenticationProvider" class="ss.EmployeeCustomAuthenticationProvider">
<property name="userDetailsService">
<bean class="ss.EmployeeUserDetailsService"/>
</property>
</bean>
<bean id="customerCustomAuthenticationProvider" class="ss.CustomerCustomAuthenticationProvider">
<property name="userDetailsService">
<bean class="ss.CustomerUserDetailsService"/>
</property>
</bean>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider ref="customerCustomAuthenticationProvider"/>
<sec:authentication-provider ref="employeeCustomAuthenticationProvider"/>
</sec:authentication-manager>
</beans>
Here's my updated configuration. It has to be some really small tweak I need to do to prevent the authentication fall back but I can't seem to figure it out now.
Thank you.
UPDATE - SOLUTION to @Ritesh's technique
Okay, I think I have solved the problem here. Instead of having EmployeeCustomAuthenticationProvider
to rely on the default UsernamePasswordAuthenticationToken
, I created EmployeeUsernamePasswordAuthenticationToken
for it, just like the one I created CustomerUsernamePasswordAuthenticationToken
for CustomerCustomAuthenticationProvider
. These providers will then override the supports()
:-
CustomerCustomAuthenticationProvider class
@Override
public boolean supports(Class<? extends Object> authentication) {
return (CustomerUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
EmployeeCustomAuthenticationProvider class
@Override
public boolean supports(Class<? extends Object> authentication) {
return (EmployeeUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
MyAuthenticationFilter class
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
...
UsernamePasswordAuthenticationToken authRequest = null;
if ("customer".equals(request.getParameter("radioAuthenticationType"))) {
authRequest = new CustomerUsernamePasswordAuthenticationToken(username, password);
}
else {
authRequest = new EmployeeUsernamePasswordAuthenticationToken(username, password);
}
setDetails(request, authRequest);
return super.getAuthenticationManager().authenticate(authRequest);
}
... and WALAA! It works perfectly now after several days of frustration!
Hopefully, this post will be able to help somebody who is doing the same thing as I am here.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您不需要创建
/j_spring_security_check_for_employee
和/j_security_check_for_customer
filterProcessingUrl
。默认的单选按钮字段创意可以很好地工作。
在自定义登录
LoginFilter
中,您需要为员工和客户创建不同的令牌。步骤如下:
使用默认的
UsernamePasswordAuthenticationToken
进行员工登录。创建用于客户登录的
CustomerAuthenticationToken
。扩展AbstractAuthenticationToken
,使其类类型与UsernamePasswordAuthenticationToken
不同。定义自定义登录过滤器:
在
customFormLoginFilter
中,重写attemptAuthentication
,如下(伪代码):重写
中的
以支持supports
方法>EmployeeCustomAuthenticationProviderUsernamePasswordAuthenticationToken
。重写
CustomerCustomAuthenticationProvider
中的supports
方法以支持CustomerAuthenticationToken
。<前><代码>@Override
公共布尔支持(类<?>身份验证){
return (CustomerAuthenticationToken.class.isAssignableFrom(authentication));
}
在
authentication-manager
中使用两个提供程序:You don't need to create
/j_spring_security_check_for_employee
and/j_security_check_for_customer
filterProcessingUrl
.The default one will work just fine with radio button field idea.
In the custom login
LoginFilter
, you need to create different tokens for employee and customer.Here are the steps:
Use default
UsernamePasswordAuthenticationToken
for employee login.Create
CustomerAuthenticationToken
for customer login. ExtendAbstractAuthenticationToken
so that its class type is distinct fromUsernamePasswordAuthenticationToken
.Define a custom login filter:
In
customFormLoginFilter
, overrideattemptAuthentication
as follows (pseudo code):Override
supports
method inEmployeeCustomAuthenticationProvider
to supportUsernamePasswordAuthenticationToken
.Override
supports
method inCustomerCustomAuthenticationProvider
to supportCustomerAuthenticationToken
.Use both providers in
authentication-manager
:您可以定义多个
AuthenticationProcessingFilter
过滤器。每个都可以有不同的 URL,例如 /j_security_check_for_employee 和 /j_security_check_for_customer。下面是演示此想法的安全应用程序上下文示例:如您所见,在这种情况下,您还有不同的
UserDetailService
- 用于 DB 身份验证和 LDAP。我认为为客户和员工提供不同的身份验证 URL 是个好主意(特别是如果他们使用不同的身份验证策略)。您甚至可以为他们设置不同的登录页面。
You can define several
AuthenticationProcessingFilter
filters. Each of them can have different URL like /j_security_check_for_employee and /j_security_check_for_customer. Here is example of the security application context that demonstrates this idea:As you can see, in this scenario you have also different
UserDetailService
s - for DB auth and LDAP.I think it's good idea to have different auth URLs for customers and employee (especially if they use different authentication strategies). You can even have different login pages for them.
对于Java配置参考,
因为我热衷于在这里写java配置方法来实现相同的技术,以帮助那些不熟悉xml配置的人,但我不想用如此长的java配置代码答案来劫持这个线程之美。
想要通过java配置(基于注释)实现相同目标的人可以参考下面给出的我的自我回答问题链接,您也可以在答案中找到我的github存储库链接以获取代码。
对于基于注释的配置代码,请参阅我的答案这里
具有不同 UsernamePasswordAuthToken 的多个 AuthenticationProvider 用于验证不同的登录表单,无需后备身份验证
For Java Configuration reference
As i keen to write here java configuration way of implementing the same technique to help people who is not familiar with xml configuration but i don't want to hijack this thread beauty with such a long answer of java configuration code.
People who wants to achieve the same with java configuration(Annotation based) can refer my self answered question link is given below and also you can find my github repository link for the code in the answer.
For Annotation based configuration code refer my answer here
Multiple AuthenticationProvider with different UsernamePasswordAuthToken to authenticate different login forms without fallback authentication
您可以将此信息存储在数据库中。例如,您可以在
Users
表中包含名为ldap_auth
的列。您可以查看我的其他答案(作为示例):Spring登录表单示例
如果您仔细查看
UserService
类,您会注意到,我实际上测试了此 LDAP 标志并从 LDAP 或数据库获取用户密码。You can store this information in DB. For example you can have column called
ldap_auth
inUsers
table. You can look at my other answer (as an example):Spring login form example
If you carefully look at
UserService
class, you will notice, that I actually test this LDAP flag and take user password either from LDAP or database.又是我:) 你可以尝试使用这样的过滤器吗:
而不是定义 bean
springSecurityFilterChain
。it's me again :) Can you try to use filters like this:
instead of defining bean
springSecurityFilterChain
.