我有一个使用Spring 4.0和Security 3.2构建的应用程序,并且我想实现会话并发性,但是它似乎不起作用。安全性的所有其他方面都工作正常。这是我的xml配置:
首先在我的web.xml中:
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
然后在我的security.xml中
<security:http auto-config="false"
use-expressions="true"
authentication-manager-ref="authManager"
access-decision-manager-ref="webAccessDecisionManager"
entry-point-ref="authenticationEntryPoint">
<security:intercept-url pattern="/agent/**" access="hasAnyRole('ROLE_AGENT')" />
<security:intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/public/**" access="permitAll" />
<security:intercept-url pattern="/**" access="permitAll" />
<security:session-management session-authentication-strategy-ref="sas"
invalid-session-url="/public/login.xhtml"/>
<security:logout logout-success-url="/public/login.xhtml"
invalidate-session="true"
delete-cookies="true"/>
<security:expression-handler ref="webExpressionHandler"/>
<security:custom-filter position="FORM_LOGIN_FILTER" ref="myAuthFilter" />
<security:custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
</security:http>
和
<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<constructor-arg index="0" value="/public/login.xhtml" />
</bean>
<bean id="customAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"
p:defaultFailureUrl="/public/login.xhtml" />
<bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl"/>
<bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
<constructor-arg index="0" ref="sessionRegistry"/>
<constructor-arg index="1" value="/session-expired.htm"/>
</bean>
<bean id="myAuthFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="sessionAuthenticationStrategy" ref="sas" />
<property name="authenticationManager" ref="authManager" />
<property name="authenticationFailureHandler" ref="customAuthenticationFailureHandler"/>
</bean>
<bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
<constructor-arg name="sessionRegistry" ref="sessionRegistry" />
<property name="maximumSessions" value="1" />
<property name="exceptionIfMaximumExceeded" value="true" />
</bean>
<bean id="authManager" class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<ref bean="myCompLdapAuthProvider"/>
<ref bean="myCompDBAuthProvider"/>
</list>
</property>
</bean>
我的UserDetails实现hashCode()和equals(),并且所有这些并发会话限制不起作用。经过一些调试会话后,我观察到在sessionRegistry中从未找到我的会话,我想这是主要原因,但是我不知道为什么!
知道我在这里做错了什么吗?
PS我的调试日志中有这样的记录:
(FilterChainProxy.java:337) - /resources/images/icons/connection_on.gif at position 2 of 11 in additional filter chain; firing Filter: 'ConcurrentSessionFilter'
(FilterChainProxy.java:337) - /resources/images/icons/connection_on.gif at position 3 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
(FilterChainProxy.java:337) - /resources/images/icons/connection_on.gif at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
(FilterChainProxy.java:337) - /resources/images/icons/connection_on.gif at position 5 of 11 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
(FilterChainProxy.java:337) - /resources/images/icons/connection_on.gif at position 6 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
(FilterChainProxy.java:337) - /resources/images/icons/connection_on.gif at position 7 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
(FilterChainProxy.java:337) - /resources/images/icons/connection_on.gif at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
(AnonymousAuthenticationFilter.java:107) - SecurityContextHolder not populated with anonymous token, as it already contained: 'org.springframework.security.authentication.UsernamePasswordAuthenticationToken@96cf68e: Principal: MyUserDetails [username=adrian.videanu, dn=org.springframework.ldap.core.DirContextAdapter: dn=cn=Adrian Videanu,ou=IT,ou=Organization .....
所以过滤器被调用...
更新
我可以看到会话创建事件已发布,因为我在日志中有此行:
(HttpSessionEventPublisher.java:66) - Publishing event: org.springframework.security.web.session.HttpSessionCreatedEvent[source=org.apache.catalina.session.StandardSessionFacade@3827a0aa]
但我从没想过应该从SessionRegistryImpl中点击registerNewSession方法。另外,当我最初打开登录页面时,还会调用HttpSessionEventPublisher,因为我猜是在创建会话时,但是在输入凭据并推送提交后,不再调用HttpSessionEventPublisher。
更新2
作为测试,我将SessionRegistryImpl注入到我的一个bean中,以尝试访问其某些方法:
@Named
@Scope("view")
public class UserDashboardMB implements Serializable {
private static final long serialVersionUID = 1L;
@Inject
private SessionRegistry sessionRegistry;
public void init(){
System.out.println("-- START INIT -- ");
List<Object> principals = sessionRegistry.getAllPrincipals();
System.out.println("Principals = "+principals);
for (Object p:principals){
System.out.println("Principal = "+p);
}
System.out.println("-- STOP INIT -- ");
}
}
输出为:
INFO:-START INIT-
INFO:Principals = []
INFO:-STOP INIT-
因此,那里没有任何内容。
更新3
我已经用Serge提供的那个替换了“ sas” bean,但是它似乎仍然不起作用。我再次启用了调试器,问题是在类UsernamePasswordAuthenticationFilter的方法doFilter()上没有我的请求得到应有的处理。这是doFilter()的一部分:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Request is to process authentication");
}
Authentication authResult;
// rest of method here
}
从我在调试器中看到的内容来看,我的请求似乎不需要auth和chain.doFilter(request,response); 被调用。
更新4
我想我找到了问题。筛选器未按预期运行,因为filterUrl参数不合适。正如我在文档中看到的:
默认情况下,此过滤器响应URL / j_spring_security_check。
但是我的登录部分是通过JSF托管的bean和操作实现的。现在,我的登录表单位于/public/login.xhtml,并且发布登录信息的URL相同。如果我将其设置为filterUrl,则会遇到问题,因为在初始形式渲染时也会被调用,并且由于没有设置用户名/密码,因此存在无限循环。
知道如何克服吗?
这是我的LoginManagedBean的样子:
@Named
@Scope("request")
public class LoginMB implements Serializable {
private static final long serialVersionUID = 1L;
@Autowired
@Qualifier("authManager")
private AuthenticationManager authenticationManager;
// setters and getters
public String login(){
FacesContext context = FacesContext.getCurrentInstance();
try {
Authentication request = new UsernamePasswordAuthenticationToken(this.getUsername(), this.getPassword());
Authentication result = authenticationManager.authenticate(request);
SecurityContextHolder.getContext().setAuthentication(result);
// perform some extra logic here and return protected page
return "/agent/dashboard.xhtml?faces-redirect=true";
} catch (AuthenticationException e) {
e.printStackTrace();
logger.error("Auth Exception ->"+e.getMessage());
FacesMessage fm = new FacesMessage("Invalid user/password");
fm.setSeverity(FacesMessage.SEVERITY_ERROR);
context.addMessage(null, fm);
}
return null;
}
}
我终于设法解决了这个问题。问题是由于我在标准弹簧过滤器和自定义jsf登录表单之间进行了混合设置。正如Serge所指出的,我在xml conf中只留下了“ sas” bean,在我的LoginMB中,我已经手动并以编程方式调用了SessionAuthenticationStrategy onAuthentication()方法。所以现在我的LoginMB看起来像:
@Named
@Scope("request")
public class LoginMB implements Serializable {
@Autowired
@Qualifier("authManager")
private AuthenticationManager authenticationManager;
@Inject
@Qualifier("sas")
private SessionAuthenticationStrategy sessionAuthenticationStrategy;
public String login(){
FacesContext context = FacesContext.getCurrentInstance();
try {
Authentication authRequest = new UsernamePasswordAuthenticationToken(this.getUsername(), this.getPassword());
Authentication result = authenticationManager.authenticate(authRequest);
SecurityContextHolder.getContext().setAuthentication(result);
HttpServletRequest httpReq = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
HttpServletResponse httpResp = (HttpServletResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse();
sessionAuthenticationStrategy.onAuthentication(result, httpReq, httpResp);
// custom logic here
return "/agent/dashboard.xhtml?faces-redirect=true";
}catch(SessionAuthenticationException sae){
sae.printStackTrace();
logger.error("Auth Exception ->"+sae.getMessage());
String userMessage = "Session auth exception!";
if (sae.getMessage().compareTo("Maximum sessions of 1 for this principal exceeded") == 0){
userMessage = "Cannot login from more than 1 location.";
}
FacesMessage fm = new FacesMessage(userMessage);
fm.setSeverity(FacesMessage.SEVERITY_FATAL);
context.addMessage(null, fm);
}
catch (AuthenticationException e) {
e.printStackTrace();
logger.error("Auth Exception ->"+e.getMessage());
FacesMessage fm = new FacesMessage("Invalid user/password");
fm.setSeverity(FacesMessage.SEVERITY_FATAL);
context.addMessage(null, fm);
}
return null;
}
现在,会话已注册,并且会话限制正在起作用。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句