最近在项目里面加入Spring Security做安全控制,参照网上的资料实现了用户、角色、权限从数据库中读取。最后出现一个问题:在将session交给Spring Security管理时,不能避免同一用户的多次登录。而我在之前在XML中定义用户和角色的时候,可以控制成功。请教一下大家是什么原因?
以下是我的各个文件的代码片段:
web.xml
- <listener>
- <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
- </listener>
- <!--
- 项目中使用SpringSecurity的过滤器-->
- <filter>
- <filter-name>springSecurityFilterChain</filter-name>
- <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>springSecurityFilterChain</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
applicationContext-Security.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
- xmlns:sec="http://www.springframework.org/schema/security"
- xsi:schemaLocation="
- http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-3.0.xsd
- http://www.springframework.org/schema/security
- http://www.springframework.org/schema/security/spring-security-3.0.xsd">
- <sec:http auto-config="true">
- <!-- 登录页无须过滤 -->
- <sec:intercept-url pattern="/login.jsp" filters="none" />
- <!-- 资源的访问权限 已转为数据库存储 -->
- <!--
- <sec:intercept-url pattern="/secure/**" access="ROLE_ADMIN" />
- <sec:intercept-url pattern="/userManager/**" access="ROLE_ADMIN" />
- -->
- <sec:form-login login-page="/login.jsp"
- authentication-failure-url="/login.jsp?error=true"
- default-target-url="/login/LoginAction_loginSuccess.action"
- authentication-success-handler-ref="successHandler"
- authentication-failure-handler-ref="failureHandler"
- always-use-default-target="false" />
- <!--尝试访问没有权限的页面时跳转的页面 -->
- <sec:access-denied-handler error-page="/accessDenied.jsp" />
- <sec:http-basic />
- <!--注销用户-->
- <sec:logout invalidate-session="true"
- logout-success-url="/login/LoginAction_loginOut.action" logout-url="/j_spring_security_logout" />
- <sec:remember-me />
- <!-- 增加一个自定义的过滤器完成对URL资源的保护控制 。-->
- <sec:custom-filter before="FILTER_SECURITY_INTERCEPTOR"
- ref="resourceSecurityInterceptor" />
- <!-- 用户登录Session控制-->
- <sec:session-management invalid-session-url="/login.jsp"
- session-authentication-error-url="/login.jsp?error=true">
- <sec:concurrency-control max-sessions="1"
- error-if-maximum-exceeded="true" expired-url="/login.jsp" />
- </sec:session-management>
- </sec:http>
- <!--认证成功-->
- <bean id="successHandler"
- class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
- <property name="defaultTargetUrl" value="/login/LoginAction_loginSuccess.action" />
- <property name="alwaysUseDefaultTargetUrl" value="false" />
- </bean>
- <!-- 认证失败 -->
- <bean id="failureHandler"
- class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
- <property name="defaultFailureUrl" value="/login/LoginAction_loginFailure.action" />
- </bean>
- <bean id="resourceSecurityInterceptor"
- class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
- <!-- 认证管理器,实现用户认证的入口 -->
- <property name="authenticationManager" ref="authenticationManager" />
- <!-- 访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 -->
- <property name="accessDecisionManager" ref="accessDecisionManager" />
- <!-- 资源源数据定义,即定义某一资源可以被哪些角色访问 -->
- <property name="securityMetadataSource"
- ref="secureResourceFilterInvocationDefinitionSource" />
- <!--<property name="observeOncePerRequest" value="false" /> -->
- </bean>
- <!-- 认证管理器 -->
- <sec:authentication-manager alias="authenticationManager">
- <!-- 认证管理器提供者 【user-service-ref】引用的服务组件,通过securityManager进行对用户信息的认证-->
- <sec:authentication-provider
- user-service-ref="userDetailsService">
- <!-- 密码采用md5加密方式加密 -->
- <sec:password-encoder base64="false" ref="passwordEncoder">
- <!-- 用username做盐值加密,防止md5字典攻击 -->
- <sec:salt-source user-property="username" />
- </sec:password-encoder>
- </sec:authentication-provider>
- </sec:authentication-manager>
- <!-- 访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 -->
- <bean id="accessDecisionManager"
- class="org.springframework.security.access.vote.AffirmativeBased">
- <property name="allowIfAllAbstainDecisions" value="false" />
- <property name="decisionVoters">
- <list>
- <bean class="org.springframework.security.access.vote.RoleVoter" />
- <bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
- </list>
- </property>
- </bean>
- <!-- 资源源数据定义,即定义某一资源可以被哪些角色访问 -->
- <bean id="secureResourceFilterInvocationDefinitionSource"
- class="com.sshframework.generic.security.filter.UserDefindInvocationSecurityMetadataSource">
- <!-- 实例化需要的参数 -->
- <constructor-arg>
- <ref bean="roleDAOImpl" />
- </constructor-arg>
- <constructor-arg>
- <ref bean="userDAOImpl" />
- </constructor-arg>
- <constructor-arg>
- <ref bean="resourceDAOImpl" />
- </constructor-arg>
- <!-- URL匹配器UrlMatcher需要的参数 -->
- <!--
- useAntPath 是否使用Apache Ant的匹配模式,即资源/userManager/**
- 和/userManager/UserManager_list.action匹配
- -->
- <property name="useAntPath">
- <value>true</value>
- </property>
- <!--
- lowercaseComparisons 是否在比较URL前将URL都转化成小写,即资源/userManager/**
- 和/UserManager/××匹配
- -->
- <property name="lowercaseComparisons">
- <value>true</value>
- </property>
- </bean>
- <!-- 用户信息服务 -->
- <bean id="userDetailsService"
- class="com.sshframework.generic.security.service.UserDetailsServiceImpl">
- <property name="userDAO" ref="userDAOImpl" />
- </bean>
- <!-- 密码加密 -->
- <bean id="passwordEncoder"
- class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" />
- </beans>
自定义的资源访问类
UserDefindInvocationSecurityMetadataSource.java
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
- import org.springframework.beans.factory.InitializingBean;
- import org.springframework.security.access.ConfigAttribute;
- import org.springframework.security.access.SecurityConfig;
- import org.springframework.security.web.FilterInvocation;
- import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
- import org.springframework.security.web.util.AntUrlPathMatcher;
- import org.springframework.security.web.util.RegexUrlPathMatcher;
- import org.springframework.security.web.util.UrlMatcher;
- import com.devtek.dao.IResourceDAO;
- import com.devtek.dao.IRoleDAO;
- import com.devtek.dao.IUserDAO;
- import com.devtek.pojo.Resource;
- import com.devtek.pojo.Role;
- import com.devtek.pojo.RoleResource;
- import com.sshframework.generic.util.LoggerHandle;
- /**
- * 此类在初始化时,应该取到所有资源及其对应角色的定义
- */
- public class UserDefindInvocationSecurityMetadataSource implements
- FilterInvocationSecurityMetadataSource, InitializingBean {
- private IUserDAO userDAO;
- private IRoleDAO roleDAO;
- private IResourceDAO resourceDAO;
- private UrlMatcher urlMatcher;
- private boolean useAntPath = true;
- private boolean lowercaseComparisons = true;
- private static Map<String, Collection<ConfigAttribute>> resourceMap = null;
- public UserDefindInvocationSecurityMetadataSource(IUserDAO userDAO,
- IRoleDAO roleDAO, IResourceDAO resourceDAO) {
- super();
- this.userDAO = userDAO;
- this.roleDAO = roleDAO;
- this.resourceDAO = resourceDAO;
- loadResourceDefine();
- }
- public void afterPropertiesSet() throws Exception {
- this.urlMatcher = new RegexUrlPathMatcher();
- if (useAntPath) {
- this.urlMatcher = new AntUrlPathMatcher();
- }
- if ("true".equals(lowercaseComparisons)) {
- if (!this.useAntPath) {
- ((RegexUrlPathMatcher) this.urlMatcher)
- .setRequiresLowerCaseUrl(true);
- }
- } else if ("false".equals(lowercaseComparisons)) {
- if (this.useAntPath) {
- ((AntUrlPathMatcher) this.urlMatcher)
- .setRequiresLowerCaseUrl(false);
- }
- }
- }
- // 得到某个资源能访问的角色
- private void loadResourceDefine() {
- resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
- List<Resource> resourceList = this.resourceDAO.getAllResource();
- for (Resource resource : resourceList) {
- Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
- String resourceStr = resource.getValue();
- Set<RoleResource> roleResourceSet = resource.getRoleResources();
- for (RoleResource roleResource : roleResourceSet) {
- Role role = roleResource.getRole();
- ConfigAttribute ca = new SecurityConfig(role.getRoleName());
- atts.add(ca);
- }
- resourceMap.put(resourceStr, atts);
- }
- LoggerHandle.log(this.getClass(), LoggerHandle.LOGGER_LEVEL_INFO,
- "开始加载系统权限", null);
- StringBuffer strConfig = new StringBuffer();
- Iterator<String> ite = resourceMap.keySet().iterator();
- while (ite.hasNext()) {
- String resURL = ite.next();
- strConfig.append("{资源访问路径URL : [" + resURL + "]");
- strConfig.append(" , 所需要的角色权限 : ");
- Collection<ConfigAttribute> attss = resourceMap.get(resURL);
- for (ConfigAttribute cg : attss) {
- strConfig.append("[" + cg.toString() + "]");
- }
- strConfig.append(" } ");
- LoggerHandle.log(this.getClass(), LoggerHandle.LOGGER_LEVEL_INFO,
- strConfig.toString(), null);
- strConfig = new StringBuffer();
- }
- LoggerHandle.log(this.getClass(), LoggerHandle.LOGGER_LEVEL_INFO,
- "加载系统权限完毕", null);
- }
- public Collection<ConfigAttribute> getAttributes(Object filter)
- throws IllegalArgumentException {
- FilterInvocation filterInvocation = (FilterInvocation) filter;
- String requestURI = filterInvocation.getRequestUrl();
- Iterator<String> ite = resourceMap.keySet().iterator();
- while (ite.hasNext()) {
- String resURL = ite.next();
- // 比较资源定义中的URL和当前请求的URL
- // resURL 资源定义中的URL
- // requestURL 当前请求的URL
- if (urlMatcher.pathMatchesUrl(resURL, requestURI)) {
- Collection<ConfigAttribute> atts = resourceMap.get(resURL);
- String colRole = "";
- for (ConfigAttribute cg : atts) {
- // System.out.println(cg.toString());
- colRole = colRole + "," + cg.toString();
- }
- if (colRole.length() > 1) {
- colRole = colRole.substring(1);
- }
- LoggerHandle.log(this.getClass(),
- LoggerHandle.LOGGER_LEVEL_INFO, "{用户请求资源URL :"
- + requestURI + " ,所需要的系统角色权限为:[" + colRole
- + "]}", null);
- return atts;
- }
- }
- return null;
- }
- public boolean supports(Class<?> clazz) {
- return true;
- }
- public Collection<ConfigAttribute> getAllConfigAttributes() {
- return new ArrayList<ConfigAttribute>();
- }
- /**
- * @return the useAntPath
- */
- public boolean isUseAntPath() {
- return useAntPath;
- }
- /**
- * @param useAntPath
- * the useAntPath to set
- */
- public void setUseAntPath(boolean useAntPath) {
- this.useAntPath = useAntPath;
- }
- /**
- * @return the lowercaseComparisons
- */
- public boolean isLowercaseComparisons() {
- return lowercaseComparisons;
- }
- /**
- * @param lowercaseComparisons
- * the lowercaseComparisons to set
- */
- public void setLowercaseComparisons(boolean lowercaseComparisons) {
- this.lowercaseComparisons = lowercaseComparisons;
- }
- }
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.AntUrlPathMatcher;
import org.springframework.security.web.util.RegexUrlPathMatcher;
import org.springframework.security.web.util.UrlMatcher;
import com.devtek.dao.IResourceDAO;
import com.devtek.dao.IRoleDAO;
import com.devtek.dao.IUserDAO;
import com.devtek.pojo.Resource;
import com.devtek.pojo.Role;
import com.devtek.pojo.RoleResource;
import com.sshframework.generic.util.LoggerHandle;
/**
* 此类在初始化时,应该取到所有资源及其对应角色的定义
*/
public class UserDefindInvocationSecurityMetadataSource implements
FilterInvocationSecurityMetadataSource, InitializingBean {
private IUserDAO userDAO;
private IRoleDAO roleDAO;
private IResourceDAO resourceDAO;
private UrlMatcher urlMatcher;
private boolean useAntPath = true;
private boolean lowercaseComparisons = true;
private static Map<String, Collection<ConfigAttribute>> resourceMap = null;
public UserDefindInvocationSecurityMetadataSource(IUserDAO userDAO,
IRoleDAO roleDAO, IResourceDAO resourceDAO) {
super();
this.userDAO = userDAO;
this.roleDAO = roleDAO;
this.resourceDAO = resourceDAO;
loadResourceDefine();
}
public void afterPropertiesSet() throws Exception {
this.urlMatcher = new RegexUrlPathMatcher();
if (useAntPath) {
this.urlMatcher = new AntUrlPathMatcher();
}
if ("true".equals(lowercaseComparisons)) {
if (!this.useAntPath) {
((RegexUrlPathMatcher) this.urlMatcher)
.setRequiresLowerCaseUrl(true);
}
} else if ("false".equals(lowercaseComparisons)) {
if (this.useAntPath) {
((AntUrlPathMatcher) this.urlMatcher)
.setRequiresLowerCaseUrl(false);
}
}
}
// 得到某个资源能访问的角色
private void loadResourceDefine() {
resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
List<Resource> resourceList = this.resourceDAO.getAllResource();
for (Resource resource : resourceList) {
Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
String resourceStr = resource.getValue();
Set<RoleResource> roleResourceSet = resource.getRoleResources();
for (RoleResource roleResource : roleResourceSet) {
Role role = roleResource.getRole();
ConfigAttribute ca = new SecurityConfig(role.getRoleName());
atts.add(ca);
}
resourceMap.put(resourceStr, atts);
}
LoggerHandle.log(this.getClass(), LoggerHandle.LOGGER_LEVEL_INFO,
"开始加载系统权限", null);
StringBuffer strConfig = new StringBuffer();
Iterator<String> ite = resourceMap.keySet().iterator();
while (ite.hasNext()) {
String resURL = ite.next();
strConfig.append("{资源访问路径URL : [" + resURL + "]");
strConfig.append(" , 所需要的角色权限 : ");
Collection<ConfigAttribute> attss = resourceMap.get(resURL);
for (ConfigAttribute cg : attss) {
strConfig.append("[" + cg.toString() + "]");
}
strConfig.append(" } ");
LoggerHandle.log(this.getClass(), LoggerHandle.LOGGER_LEVEL_INFO,
strConfig.toString(), null);
strConfig = new StringBuffer();
}
LoggerHandle.log(this.getClass(), LoggerHandle.LOGGER_LEVEL_INFO,
"加载系统权限完毕", null);
}
public Collection<ConfigAttribute> getAttributes(Object filter)
throws IllegalArgumentException {
FilterInvocation filterInvocation = (FilterInvocation) filter;
String requestURI = filterInvocation.getRequestUrl();
Iterator<String> ite = resourceMap.keySet().iterator();
while (ite.hasNext()) {
String resURL = ite.next();
// 比较资源定义中的URL和当前请求的URL
// resURL 资源定义中的URL
// requestURL 当前请求的URL
if (urlMatcher.pathMatchesUrl(resURL, requestURI)) {
Collection<ConfigAttribute> atts = resourceMap.get(resURL);
String colRole = "";
for (ConfigAttribute cg : atts) {
// System.out.println(cg.toString());
colRole = colRole + "," + cg.toString();
}
if (colRole.length() > 1) {
colRole = colRole.substring(1);
}
LoggerHandle.log(this.getClass(),
LoggerHandle.LOGGER_LEVEL_INFO, "{用户请求资源URL :"
+ requestURI + " ,所需要的系统角色权限为:[" + colRole
+ "]}", null);
return atts;
}
}
return null;
}
public boolean supports(Class<?> clazz) {
return true;
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return new ArrayList<ConfigAttribute>();
}
/**
* @return the useAntPath
*/
public boolean isUseAntPath() {
return useAntPath;
}
/**
* @param useAntPath
* the useAntPath to set
*/
public void setUseAntPath(boolean useAntPath) {
this.useAntPath = useAntPath;
}
/**
* @return the lowercaseComparisons
*/
public boolean isLowercaseComparisons() {
return lowercaseComparisons;
}
/**
* @param lowercaseComparisons
* the lowercaseComparisons to set
*/
public void setLowercaseComparisons(boolean lowercaseComparisons) {
this.lowercaseComparisons = lowercaseComparisons;
}
}
用户信息访问服务类
UserDetailsServiceImpl.java
- import java.util.Collection;
- import java.util.ArrayList;
- import java.util.Set;
- import org.springframework.dao.DataAccessException;
- import org.springframework.security.core.GrantedAuthority;
- import org.springframework.security.core.authority.GrantedAuthorityImpl;
- import org.springframework.security.core.userdetails.UserDetails;
- import org.springframework.security.core.userdetails.UserDetailsService;
- import org.springframework.security.core.userdetails.UsernameNotFoundException;
- import com.devtek.dao.IUserDAO;
- import com.devtek.pojo.Role;
- import com.devtek.pojo.User;
- import com.devtek.pojo.UserRole;
- /**
- * 实现SpringSecurity的UserDetailsService接口,实现获取用户信息UserDetails.
- *
- */
- public class UserDetailsServiceImpl implements UserDetailsService {
- private IUserDAO userDAO;
- public UserDetails loadUserByUsername(String userName)
- throws UsernameNotFoundException, DataAccessException {
- User user = this.userDAO.getUserByUserName(userName);
- return user;
- }
- public IUserDAO getUserDAO() {
- return userDAO;
- }
- public void setUserDAO(IUserDAO userDAO) {
- this.userDAO = userDAO;
- }
- }
import java.util.Collection;
import java.util.ArrayList;
import java.util.Set;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.devtek.dao.IUserDAO;
import com.devtek.pojo.Role;
import com.devtek.pojo.User;
import com.devtek.pojo.UserRole;
/**
* 实现SpringSecurity的UserDetailsService接口,实现获取用户信息UserDetails.
*
*/
public class UserDetailsServiceImpl implements UserDetailsService {
private IUserDAO userDAO;
public UserDetails loadUserByUsername(String userName)
throws UsernameNotFoundException, DataAccessException {
User user = this.userDAO.getUserByUserName(userName);
return user;
}
public IUserDAO getUserDAO() {
return userDAO;
}
public void setUserDAO(IUserDAO userDAO) {
this.userDAO = userDAO;
}
}
用户信息类,实现了UserDetails 接口
User.java
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.HashSet;
- import java.util.Set;
- import org.springframework.security.core.GrantedAuthority;
- import org.springframework.security.core.authority.GrantedAuthorityImpl;
- import org.springframework.security.core.userdetails.UserDetails;
- public class User implements java.io.Serializable, UserDetails {
- // Fields
- private String userId;
- private String userName;
- private String passWord;
- private String sex;
- private Integer status;
- private String descn;
- private Set userRoles = new HashSet(0);
- // Constructors
- public User() {
- }
- public User(String userId) {
- this.userId = userId;
- }
- public User(String userId, String userName, String passWord, String sex,
- Integer status, String descn, Set userRoles) {
- this.userId = userId;
- this.userName = userName;
- this.passWord = passWord;
- this.sex = sex;
- this.status = status;
- this.descn = descn;
- this.userRoles = userRoles;
- }
- // Property accessors
- set() get()方法略.........
- // 扩展UserDetails需要实现的方法---------------------------------------------------------//
- public Collection<GrantedAuthority> getAuthorities() {
- // TODO Auto-generated method stub
- Collection<GrantedAuthority> authCol = new ArrayList<GrantedAuthority>();
- Set<UserRole> userRoleSet = this.getUserRoles();
- for (UserRole ur : userRoleSet) {
- Role role = ur.getRole();
- authCol.add(new GrantedAuthorityImpl(role.getRoleName()));
- }
- return authCol;
- }
- public String getPassword() {
- return this.passWord;
- }
- public String getUsername() {
- return this.userName;
- }
- public boolean isAccountNonExpired() {
- return true;
- }
- public boolean isAccountNonLocked() {
- return true;
- }
- public boolean isCredentialsNonExpired() {
- return true;
- }
- public boolean isEnabled() {
- return true;
- }
- // 扩展UserDetails需要实现的方法---------------------------------------------------------//
- }
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.UserDetails;
public class User implements java.io.Serializable, UserDetails {
// Fields
private String userId;
private String userName;
private String passWord;
private String sex;
private Integer status;
private String descn;
private Set userRoles = new HashSet(0);
// Constructors
public User() {
}
public User(String userId) {
this.userId = userId;
}
public User(String userId, String userName, String passWord, String sex,
Integer status, String descn, Set userRoles) {
this.userId = userId;
this.userName = userName;
this.passWord = passWord;
this.sex = sex;
this.status = status;
this.descn = descn;
this.userRoles = userRoles;
}
// Property accessors
set() get()方法略.........
// 扩展UserDetails需要实现的方法---------------------------------------------------------//
public Collection<GrantedAuthority> getAuthorities() {
// TODO Auto-generated method stub
Collection<GrantedAuthority> authCol = new ArrayList<GrantedAuthority>();
Set<UserRole> userRoleSet = this.getUserRoles();
for (UserRole ur : userRoleSet) {
Role role = ur.getRole();
authCol.add(new GrantedAuthorityImpl(role.getRoleName()));
}
return authCol;
}
public String getPassword() {
return this.passWord;
}
public String getUsername() {
return this.userName;
}
public boolean isAccountNonExpired() {
return true;
}
public boolean isAccountNonLocked() {
return true;
}
public boolean isCredentialsNonExpired() {
return true;
}
public boolean isEnabled() {
return true;
}
// 扩展UserDetails需要实现的方法---------------------------------------------------------//
}
其他pojo类省略(Role.java UserRole.java Resource.java RoleResource.java)
问题:现在能实现访问/userManager/UserManager_listUser.aciton 时跳转到login.jsp,输入用户名密码正常经/login/LoginAction_loginSuccess.action 再转到UserManager_listUser.aciton。但是多浏览器测试时,不能控制同一用户只允许登录一次。即代码中部门配置的session管理不起作用。困扰很久了。。。望各位指教!
PS:我的环境 Struts2+Hibernate3+Spring3 ,Spring Security 是3.0.5版本。