我已经实现了自定义 UserDetail 和 AuthenticationProvider 以与需要超过 1 个参数的自定义基本登录表单一起使用。一切正常,但并发控制除外,它不会阻止用户多次登录或使用多个设备。
这是我正在使用的配置:
网络.xml
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>kh.com.gfam.rsos.listener.InitializeApplicationListner</listener-class>
</listener>
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<multipart-config>
<max-file-size>10485760</max-file-size>
<max-request-size>104857600</max-request-size>
<file-size-threshold>20971520</file-size-threshold>
</multipart-config>
</servlet>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j.properties</param-value>
</context-param>
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>rsos.root</param-value>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.png</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<servlet>
<description></description>
<display-name>GetImageController</display-name>
<servlet-name>GetImageController</servlet-name>
<servlet-class>kh.com.gfam.rsos.presentation.controller.GetImage.GetImageController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>GetImageController</servlet-name>
<url-pattern>/GetImageController</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>home</welcome-file>
</welcome-file-list>
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<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>
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
spring-security.xml
<http auto-config="false" use-expressions="true"
entry-point-ref="loginUrlAuthenticationEntryPoint">
<!-- omitted -->
<intercept-url pattern="/Admin/**" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/Concierge/**" access="hasRole('ROLE_USER')" />
<intercept-url pattern="/Login" access="permitAll" />
<intercept-url pattern="/**" access="permitAll" />
<custom-filter position="FORM_LOGIN_FILTER"
ref="companyIdUsernamePasswordAuthenticationFilter" />
<custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
<logout logout-url="/Logout" delete-cookies="true"
invalidate-session="true" success-handler-ref="RsosLogoutSuccessHandler" />
<csrf disabled="true" />
<session-management invalid-session-url="/Login"
session-authentication-strategy-ref="sas"/>
</http>
<global-method-security secured-annotations="enabled"/>
<authentication-manager alias="authenticationManager">
<authentication-provider
ref="companyIdUsernamePasswordAuthenticationProvider" />
</authentication-manager>
spring-config.xml
<bean id="companyIdUsernamePasswordAuthenticationProvider"
class="kh.com.gfam.rsos.common.security.RsosAuthenticationProvider" />
<bean id="companyIdUsernamePasswordAuthenticationFilter"
class="kh.com.gfam.rsos.common.security.RsosUsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager" />
<property name="sessionAuthenticationStrategy" ref="sas" />
<property name="authenticationFailureHandler" ref="authenticationFailureHandler" />
<property name="authenticationSuccessHandler" ref="authenticationSuccessHandler" />
<property name="filterProcessesUrl" value="/Authenticate" />
</bean>
<bean id="loginUrlAuthenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<constructor-arg value="/Login" />
</bean>
<bean id="authenticationFailureHandler"
class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/Login/defaultError" />
<property name="exceptionMappings">
<props>
<prop
key="org.springframework.security.authentication.BadCredentialsException">
/Login/badCredentials
</prop>
<prop
key="org.springframework.security.core.userdetails.UsernameNotFoundException">
/Login/usernameNotFound
</prop>
<prop
key="org.springframework.security.authentication.DisabledException">
/Login/disabled
</prop>
<prop
key="org.springframework.security.authentication.ProviderNotFoundException">
/Login/providerNotFound
</prop>
<prop
key="org.springframework.security.authentication.AuthenticationServiceException">
/Login/authenticationService
</prop>
</props>
</property>
</bean>
<bean id="authenticationSuccessHandler"
class="kh.com.gfam.rsos.common.security.RsosAuthenticationSuccessHandler">
</bean>
<bean id="RsosLogoutSuccessHandler"
class="kh.com.gfam.rsos.common.security.RsosLogoutSucessHandler"></bean>
<bean id="concurrencyFilter"
class="org.springframework.security.web.session.ConcurrentSessionFilter">
<constructor-arg name="sessionRegistry" ref="sessionRegistry" />
<constructor-arg name="expiredUrl" value="/session-expired.jsp" />
</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="sessionRegistry"
class="org.springframework.security.core.session.SessionRegistryImpl"/>
根上下文.xml
<import resource="classpath:springConfig.xml" />
<import resource="appServlet/servlet-context.xml" />
<import resource="appServlet/spring-security.xml" />
<bean id="InitializationService"
class="kh.com.gfam.rsos.businesslogic.initialization.impl.InitializationServiceImpl">
</bean>
自定义 AuthenticationProvider 类
public class RsosAuthenticationProvider implements AuthenticationProvider {
@Autowired
LoginService service;
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
RsosUsernamePasswordAuthenticationToken auth = (RsosUsernamePasswordAuthenticationToken) authentication;
String user_id = String.valueOf(auth.getName());
String password = String.valueOf(auth.getCredentials());
int hotel_code = auth.getHotel_code();
int user_type = auth.getUser_type();
UserDTO user = null;
user = service.authenicate(hotel_code, user_id, password, user_type);
if (user.getUser_type() == 1) {
return new UsernamePasswordAuthenticationToken(user, authentication.getCredentials(),
Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")));
} else {
return new UsernamePasswordAuthenticationToken(user, authentication.getCredentials(),
Collections.singletonList(new SimpleGrantedAuthority("ROLE_ADMIN")));
}
}
@Override
public boolean supports(Class<?> authentication) {
return RsosUsernamePasswordAuthenticationToken.class
.equals(authentication);
}
}
自定义用户名密码AuthenticationToken
public class RsosUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken {
private static final long serialVersionUID = 9103166337373681531L;
private final int hotel_code;
private final int user_type;
public RsosUsernamePasswordAuthenticationToken(Object principal, Object credentials,
int hotel_code, int user_type) {
super(principal, credentials);
this.hotel_code = hotel_code;
this.user_type = user_type;
}
public RsosUsernamePasswordAuthenticationToken(Object principal, Object credentials,
int hotel_code, int user_type,
Collection<? extends GrantedAuthority> authorities) {
super(principal, credentials, authorities);
this.hotel_code = hotel_code;
this.user_type = user_type;
}
public int getHotel_code() {
return hotel_code;
}
public int getUser_type() {
return user_type;
}
}
自定义用户名密码身份验证过滤器
public class RsosUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported:"
+ request.getMethod());
}
String username = super.obtainUsername(request);
String password = super.obtainPassword(request);
int hotel_code = Integer.parseInt(ObtainHotelCode(request));
int user_type = Integer.parseInt(ObtainUserType(request));
if (!StringUtils.hasText(hotel_code + "")) {
throw new AuthenticationServiceException("Hotel Code is require");
}
RsosUsernamePasswordAuthenticationToken authrequest =
new RsosUsernamePasswordAuthenticationToken(username, password, hotel_code, user_type); // (1)
setDetails(request, authrequest);
return this.getAuthenticationManager().authenticate(authrequest);
}
protected String ObtainHotelCode(HttpServletRequest request) {
return request.getParameter("hotel_code");
}
protected String ObtainUserType(HttpServletRequest request) {
return request.getParameter("user_type");
}
}
自定义 AuthenticationSuccessHandler
public class RsosAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
UserDTO authUser = (UserDTO) SecurityContextHolder.getContext().getAuthentication()
.getPrincipal();
request.getSession().setAttribute("userData", authUser);
response.setStatus(HttpServletResponse.SC_OK);
if (authUser.getUser_type() == 1) {
response.sendRedirect(request.getContextPath() + "/Concierge/New_Arrival");
} else {
response.sendRedirect(request.getContextPath() + "/Admin/Main_Info");
}
}
}
自定义 LogoutSucessHandler
public class RsosLogoutSucessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
if (authentication != null && authentication.getDetails() != null) {
try {
request.getSession().invalidate();
} catch (Exception e) {
e.printStackTrace();
e = null;
}
}
response.setStatus(HttpServletResponse.SC_OK);
response.sendRedirect(request.getContextPath() + "/Login");
}
}
自定义 UserDetail 类
public class UserDTO implements UserDetails {
/** */
private static final long serialVersionUID = -2228367483835088451L;
/** Hotel Code */
private int hotel_code;
/** UserID */
// @Size(min=4, max=4) @Pattern(regexp = "[0-9]")
private String user_id;
/** User Name */
private String user_name;
/** Password */
@NotNull
@Size(min = 8, max = 30)
private String password;
/** User Type */
@NotNull
private int user_type;
//set() .... get()//
public void setUser_name(String user_name) {
this.user_name = user_name;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getUsername() {
return user_id;
}
@Override
public boolean isAccountNonExpired() {
return false;
}
@Override
public boolean isAccountNonLocked() {
return false;
}
@Override
public boolean isCredentialsNonExpired() {
return false;
}
@Override
public boolean isEnabled() {
return false;
}
@Override
public boolean equals(Object rhs) {
if (rhs instanceof UserDTO) {
return (user_id.equals(((UserDTO) rhs).user_id));
}
return false;
}
/**
* Returns the hashcode of the {@code username}.
*/
@Override
public int hashCode() {
return user_id.hashCode();
}
}
这是登录表单的样子
我是 spring security 的新手,所以这里可能出了什么问题?有人可以指出错误吗? 谢谢。
最佳答案
在阅读有关如何从 Spring 3.2 迁移到 4.x 的 Spring 文档后,我已经解决了这个问题 http://docs.spring.io/spring-security/site/migrate/current/3-to-4/html5/migrate-3-to-4-xml.html#m3to4-deprecations-web-cscs
我从
<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="sas"
class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">
<constructor-arg>
<list>
<bean
class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
<constructor-arg ref="sessionRegistry" />
<property name="maximumSessions" value="1" />
<property name="exceptionIfMaximumExceeded" value="true" />
</bean>
<bean
class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy" />
<bean
class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy">
<constructor-arg ref="sessionRegistry" />
</bean>
</list>
</constructor-arg>
</bean>
使用Spring 4.x遇到同样问题的 friend ,你可以尝试阅读上面链接中的全文,应该也能解决你的问题。
关于java - 无法阻止用户使用带有自定义 UserDetail 和 AuthenticationProvider 的 spring security 多次登录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31022909/