Spring Security学习笔记之UsernamePasswordAuthenticationFilter, ConcurrentSessionFilter

UsernamePasswordAuthenticationFilter主要用来处理用户登录时的验证操作. 它的一般用法请参考Spring Security学习笔记之整体配置

ConcurrentSessionFilter的作用比较简单, 它会对每一个请求都作判断:
1) 如果session没过期, 就会更新session里的"last update" date/time;
2) 如果session过期, 就会调用logout handlers(一般是LogoutFilter)去销毁session, 然后跳转到expiredUrl;

(注意这里的session是指储存在SessionRegistry里的SessionInformation实例, 不是HttpSession)

ConcurrentSessionFilter的构造函数需要两个参数(第二个可以省略)
sessionRegistry: 一般是SessionRegistryImpl的实例
expiredUrl: session过期后跳转的页面



这里主要介绍一下如何使用这两个过滤器来防止用户重复登录的问题.

UsernamePasswordAuthenticationFilter的父类AbstractAuthenticationProcessingFilter有一个属性sessionStrategy, 就是用它来指定具体的防止重复登录的策略. 它的默认值是NullAuthenticatedSessionStrategy. NullAuthenticatedSessionStrategy只是一个抽象类, 不做任何操作, 源码如下:

public final class NullAuthenticatedSessionStrategy implements SessionAuthenticationStrategy {

    public void onAuthentication(Authentication authentication, HttpServletRequest request,
            HttpServletResponse response) {
    }
}


我们一般会把sessionStrategy绑定到一个CompositeSessionAuthenticationStrategy的实例. CompositeSessionAuthenticationStrategy只是一个代理类, 也不做具体的操作. 具体的操作会交给它的delegateStrategies属性所指定的 所有SessionAuthenticationStrategy实例来操作, 常用的有RegisterSessionAuthenticationStrategy和ConcurrentSessionControlAuthenticationStrategy.


注意, delegateStrategies 是一个集合, 可绑定多个SessionAuthenticationStrategy的实例:

public class CompositeSessionAuthenticationStrategy implements SessionAuthenticationStrategy {
    private final Log logger = LogFactory.getLog(getClass());
    private final List<SessionAuthenticationStrategy> delegateStrategies;
	...
}

绑定的配置如下:

<bean id="loginAuthenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">  
    ...  
    <property name="sessionAuthenticationStrategy" ref="compositeSessionAuthenticationStrategy"></property>  
</bean>  
  
<bean id="compositeSessionAuthenticationStrategy" class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">  
    <constructor-arg>  
        <list>  
            <bean id="registerSessionAuthenticationStrategy" class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy">  
                <constructor-arg ref="sessionRegistry"/>  
            </bean>  
            <bean id="concurrentSessionControlAuthenticationStrategy" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">  
                <constructor-arg ref="sessionRegistry"/>  
                <property name="maximumSessions" value="1"></property> <!-- 同一个用户最多允许好多少个session -->  
				
		<!-- exceptionIfMaximumExceeded, 当超过最大session数时:
			true: 不允许新session, 保持旧session
			false: 销毁旧session, 新session生效
		-->
                <property name="exceptionIfMaximumExceeded" value="true"></property>
            </bean>  
        </list>  
    </constructor-arg>  
</bean>  
  
<bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl"/> 

可以看到, RegisterSessionAuthenticationStrategy和ConcurrentSessionControlAuthenticationStrategy都需要有一个SessionRegistry的实例来作为构造函数的参数. 一般用SessionRegistryImpl就可以了. 至于SessionRegistryImpl具体做了什么, 后面会介绍.


现在让我们先来看看AbstractAuthenticationProcessingFilter在用户登录时做了什么.

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;

	try {
		// 这里调用了子类UsernamePasswordAuthenticationFilter.attemptAuthentication()方法来验证用户是否存在
		authResult = attemptAuthentication(request, response);
		if (authResult == null) {
			// return immediately as subclass has indicated that it hasn't completed authentication
			return;
		}
		
		// 如果用户信息正确, 则继续做下一步验证(如防止重复登录验证)
		sessionStrategy.onAuthentication(authResult, request, response);
	} catch(InternalAuthenticationServiceException failed) {
		logger.error("An internal error occurred while trying to authenticate the user.", failed);
		unsuccessfulAuthentication(request, response, failed);

		return;
	}
	catch (AuthenticationException failed) {
		// Authentication failed
		unsuccessfulAuthentication(request, response, failed);

		return;
	}

	// Authentication success
	if (continueChainBeforeSuccessfulAuthentication) {
		chain.doFilter(request, response);
	}

	successfulAuthentication(request, response, chain, authResult);
}

这里主要看上面的注释部分. 在验证完用户信息后, 它会调用轮流调用上面我们配置的sessionStrategy(即RegisterSessionAuthenticationStrategy和ConcurrentSessionControlAuthenticationStrategy)的onAuthentication()方法来做下一步验证.

先看RegisterSessionAuthenticationStrategy的onAuthentication()方法:

public void onAuthentication(Authentication authentication, HttpServletRequest request,
            HttpServletResponse response) {
        sessionRegistry.registerNewSession(request.getSession().getId(), authentication.getPrincipal());
    }


它只是简单的调用了实现类SessionRegistryImpl的registerNewSession()方法:

public void registerNewSession(String sessionId, Object principal) {
	Assert.hasText(sessionId, "SessionId required as per interface contract");
	Assert.notNull(principal, "Principal required as per interface contract");

	if (logger.isDebugEnabled()) {
		logger.debug("Registering session " + sessionId +", for principal " + principal);
	}

	if (getSessionInformation(sessionId) != null) {
		removeSessionInformation(sessionId);
	}

	sessionIds.put(sessionId, new SessionInformation(principal, sessionId, new Date()));

	Set<String> sessionsUsedByPrincipal = principals.get(principal);

	if (sessionsUsedByPrincipal == null) {
		sessionsUsedByPrincipal = new CopyOnWriteArraySet<String>();
		
		// 把用户信息和session信息作对应关系, 并保存起来
		Set<String> prevSessionsUsedByPrincipal = principals.putIfAbsent(principal, sessionsUsedByPrincipal);
		if (prevSessionsUsedByPrincipal != null) {
			sessionsUsedByPrincipal = prevSessionsUsedByPrincipal;
		}
	}

	sessionsUsedByPrincipal.add(sessionId);

	if (logger.isTraceEnabled()) {
		logger.trace("Sessions used by '" + principal + "' : " + sessionsUsedByPrincipal);
	}
}

它主要的作用是把当前的用户信息和当前的session信息作了对应关系, 然后存在principals属性里. 如果该用户再次登录的话, 那么这个用户信息就会有两个对应的session信息了, 如此类推.

接着看ConcurrentSessionControlAuthenticationStrategy的onAuthentication()方法:

public void onAuthentication(Authentication authentication, HttpServletRequest request,
		HttpServletResponse response) {

	// 找出当前用户所对应的所有session信息
	final List<SessionInformation> sessions = sessionRegistry.getAllSessions(authentication.getPrincipal(), false);

	int sessionCount = sessions.size();
	int allowedSessions = getMaximumSessionsForThisUser(authentication);

	if (sessionCount < allowedSessions) {
		// They haven't got too many login sessions running at present
		return;
	}

	if (allowedSessions == -1) {
		// We permit unlimited logins
		return;
	}

	if (sessionCount == allowedSessions) {
		HttpSession session = request.getSession(false);

		if (session != null) {
			// Only permit it though if this request is associated with one of the already registered sessions
			for (SessionInformation si : sessions) {
				if (si.getSessionId().equals(session.getId())) {
					return;
				}
			}
		}
		// If the session is null, a new one will be created by the parent class, exceeding the allowed number
	}

	allowableSessionsExceeded(sessions, allowedSessions, sessionRegistry);
}

逻辑很简单, 它会找出当前用户所对应的所有session信息, 如果数量大于最大的允许数, 就抛出异常(如果exceptionIfMaximumExceeded=true).


但这里有一个问题, 它是怎样找出当前用户所对应的所有session信息的呢? 上面我们说到, 它把对应信息存在了SessionRegistryImpl的principals属性里. 下面我们看看SessionRegistryImpl的getAllSessions()方法, 通过它来找出用户的所有session信息.

public List<SessionInformation> getAllSessions(Object principal, boolean includeExpiredSessions) {
	// 找出用户的所有session信息
	final Set<String> sessionsUsedByPrincipal = principals.get(principal);

	if (sessionsUsedByPrincipal == null) {
		return Collections.emptyList();
	}

	List<SessionInformation> list = new ArrayList<SessionInformation>(sessionsUsedByPrincipal.size());

	for (String sessionId : sessionsUsedByPrincipal) {
		SessionInformation sessionInformation = getSessionInformation(sessionId);

		if (sessionInformation == null) {
			continue;
		}

		if (includeExpiredSessions || !sessionInformation.isExpired()) {
			list.add(sessionInformation);
		}
	}

	return list;
}

principals是一个ConcurrentHashMap的实例, 它的key是我们定义的User类的实例. 如果我们的User类没有重写equals()和hashCode()方法, 那么即使一个人登录了两次, 在他第二次登录的时候, 这里principals.get(principal)只会拿到他第二次登录的session信息, 而不是第一次和第二次的session信息, 所以他第二次还能正常登录.

所以, 我们要重写User类的equals()和hashCode()方法, 来区分什么情况下是同一个用户在登录(这里我判断如果用相同的username和password来登录, 就是同一个用户).

@Override
public boolean equals(Object obj) {
	if (obj instanceof User && this.hashCode() == obj.hashCode()) {
		return true;
	} else {
		return false;
	}
}

@Override
public int hashCode() {
	return this.userName.hashCode() + this.password.hashCode();
}

写到这里, 防止重复登录的功能基本上就实现了. 但官方文档上有一个提示 21.3 Concurrency Control, 一定要在web.xml加上下面这个监听器:

<listener>
	<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
理由是:

<quote>

Adding the listener to web.xml causes an ApplicationEvent to be published to the Spring ApplicationContext every time a HttpSession commences or terminates. This is critical, as it allows the SessionRegistryImpl to be notified when a session ends. Without it, a user will never be able to log back in again once they have exceeded their session allowance, even if they log out of another session or it times out.

</quote>

意思是说, 每当一个session结束的时候, 该监听器都会通知SessionRegistryImpl来删除这个session的信息. 这是因为SessionRegistryImpl实现了ApplicationListener<SessionDestroyedEvent>接口. 当一个session结束时, Spring容器会自动通知它, 然后它会调用onApplicationEvent()方法来删除对应的session信息.

public class SessionRegistryImpl implements SessionRegistry, ApplicationListener<SessionDestroyedEvent> {
	...
	
	public void onApplicationEvent(SessionDestroyedEvent event) {
        String sessionId = event.getId();
        removeSessionInformation(sessionId);
    }
}
如果不加这个监听器, 那么当一个用户的session超时后, 他将永远不能再登录.

现在, 防止用户重复登录的功能已经实现了. 但是,, 上面我们设置ConcurrentSessionControlAuthenticationStrategy的属性exceptionIfMaximumExceeded为true:

<bean id="concurrentSessionControlAuthenticationStrategy" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
	<constructor-arg ref="sessionRegistry"/>
	<property name="exceptionIfMaximumExceeded" value="false"/>
</bean>
意思是, 当用户第一次登录的session未过期时, 他的第二次登录会失败. 那么会引发一个问题: 如果用户没有点击Logout按钮进行登出, 而是直接关闭浏览器. 那么, 在他第一个session超时之前, 他都不能正常登录...

解决的方法是, 把exceptionIfMaximumExceeded设为false. 它的意思是, 允许用户进行第二次登录, 登录成功后, 会销毁第一次登录的session, 以保证每个用户同一时间只有一个有效的session. 

但是, 如果把exceptionIfMaximumExceeded设为false, 我们要额外设置多一个过滤器 -- ConcurrentSessionFilter:

<sec:http entry-point-ref="myAuthenticationEntryPoint">
	...
	<sec:custom-filter ref="concurrencySessionFilter" position="CONCURRENT_SESSION_FILTER"/>
</sec:http>

<bean id="concurrencySessionFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
	<constructor-arg name="sessionRegistry" ref="sessionRegistry" />
	<constructor-arg name="expiredUrl" value="/login" />
</bean>
这个过滤器会销毁第一次登录时的session.


那么它是如何实现的呢? 通过观察上面ConcurrentSessionControlAuthenticationStrategy的onAuthentication()方法我们发现, 如果当前用户的session数大于设置的最大数时, 它会调用allowableSessionsExceeded() 方法:

protected void allowableSessionsExceeded(List<SessionInformation> sessions, int allowableSessions,
		SessionRegistry registry) throws SessionAuthenticationException {
	if (exceptionIfMaximumExceeded || (sessions == null)) {
		throw new SessionAuthenticationException(messages.getMessage("ConcurrentSessionControlAuthenticationStrategy.exceededAllowed",
				new Object[] {Integer.valueOf(allowableSessions)},
				"Maximum sessions of {0} for this principal exceeded"));
	}

	// Determine least recently used session, and mark it for invalidation
	SessionInformation leastRecentlyUsed = null;

	for (SessionInformation session : sessions) {
		if ((leastRecentlyUsed == null)
				|| session.getLastRequest().before(leastRecentlyUsed.getLastRequest())) {
			leastRecentlyUsed = session;
		}
	}

	// 把旧的session设成过期
	leastRecentlyUsed.expireNow();
}
这个方法会把旧的session设成过期. 

(注意这里的leastRecentlyUsed是SessionInformation的实例, 不是HttpSession的实例, 所以我们才要配多一个ConcurrentSessionFilter. 如果不配的话, 则旧的session会仍然有效)

下面是ConcurrentSessionFilter的doFilter()方法:

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
		throws IOException, ServletException {
	HttpServletRequest request = (HttpServletRequest) req;
	HttpServletResponse response = (HttpServletResponse) res;

	HttpSession session = request.getSession(false);

	if (session != null) {
		SessionInformation info = sessionRegistry.getSessionInformation(session.getId());

		if (info != null) {
			// 如果session已过期, 则logout, 并且跳转到expiredUrl
			if (info.isExpired()) {
				// Expired - abort processing
				doLogout(request, response);

				String targetUrl = determineExpiredUrl(request, info);

				if (targetUrl != null) {
					redirectStrategy.sendRedirect(request, response, targetUrl);

					return;
				} else {
					response.getWriter().print("This session has been expired (possibly due to multiple concurrent " +
							"logins being attempted as the same user).");
					response.flushBuffer();
				}

				return;
			} else {
				// Non-expired - update last request date/time
				sessionRegistry.refreshLastRequest(info.getSessionId());
			}
		}
	}

	chain.doFilter(request, response);
}
logout操作会交给我们配置的LogoutFilter来做, 它会把对应的session销毁. 具体请参考 Spring Security学习笔记之LogoutFilter

-------------------------------------------

补充

接口Authentication和UserDetails的区别:

Authentication: 它存储安全实体的标识, 密码以及认证请求的上下文信息. 它还包含用户认证后的信息(可能会包含一个UserDetails的实例). 通常不会被扩展, 除非是为了支持某种特定类型的认证.

UserDetails: 为了存储一个安全实体的概况信息, 包含名字, e-mail, 电话号码等. 通常会被扩展以支持业务需求.

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
序言 I. 入门 1. 介绍 1.1. Spring Security是什么? 1.2. 历史 1.3. 发行版本号 1.4. 获得Spring Security 1.4.1. 项目模块 1.4.1.1. Core - spring-security-core.jar 1.4.1.2. Web - spring-security-web.jar 1.4.1.3. Config - spring-security-config.jar 1.4.1.4. LDAP - spring-security-ldap.jar 1.4.1.5. ACL - spring-security-acl.jar 1.4.1.6. CAS - spring-security-cas-client.jar 1.4.1.7. OpenID - spring-security-openid.jar 1.4.2. 获得源代码 2. Security命名空间配置 2.1. 介绍 2.1.1. 命名空间的设计 2.2. 开始使用安全命名空间配置 2.2.1. 配置web.xml 2.2.2. 最小 <http> 配置 2.2.2.1. auto-config 包含了什么? 2.2.2.2. 表单和基本登录选项 2.2.3. 使用其他认证提供器 2.2.3.1. 添加一个密码编码器 2.3. 高级web特性 2.3.1. Remember-Me认证 2.3.2. 添加HTTP/HTTPS信道安全 2.3.3. 会话管理 2.3.3.1. 检测超 2.3.3.2. 同步会话控制 2.3.3.3. 防止Session固定攻击 2.3.4. 对OpenID的支持 2.3.4.1. 属性交换 2.3.5. 添加你自己的filter 2.3.5.1. 设置自定义 AuthenticationEntryPoint 2.4. 保护方法 2.4.1. <global-method-security> 元素 2.4.1.1. 使用protect-pointcut 添加安全切点 2.5. 默认的AccessDecisionManager 2.5.1. 自定义AccessDecisionManager 2.6. 验证管理器和命名空间 3. 示例程序 3.1. Tutorial示例 3.2. Contacts 3.3. LDAP例子 3.4. CAS例子 3.5. Pre-Authentication例子 4. Spring Security社区 4.1. 任务跟踪 4.2. 成为参与者 4.3. 更多信息 II. 结构和实现 5. 技术概述 5.1. 运行环境 5.2. 核心组件 5.2.1. SecurityContextHolder, SecurityContext 和 Authentication对象 5.2.1.1. 获得当前用户的信息 5.2.2. UserDetailsService 5.2.3. GrantedAuthority 5.2.4. 小结 5.3. 验证 5.3.1. 什么是Spring Security的验证呢? 5.3.2. 直接设置SecurityContextHolder的内容 5.4. 在web应用中验证 5.4.1. ExceptionTranslationFilter 5.4.2. AuthenticationEntryPoint 5.4.3. 验证机制 5.4.4. 在请求之间保存SecurityContext 。 5.5. Spring Security中的访问控制(验证) 5.5.1. 安全和AOP建议 5.5.2. 安全对象和AbstractSecurityInterceptor 5.5.2.1. 配置属性是什么? 5.5.2.2. RunAsManager 5.5.2.3. AfterInvocationManager 5.5.2.4. 扩展安全对象模型 5.6. 国际化 6. 核心服务 6.1. The AuthenticationManager , ProviderManager 和 AuthenticationProvider s 6.1.1. DaoAuthenticationProvider 6.2. UserDetailsService 实现 6.2.1. 内存认证 6.2.2. JdbcDaoImpl 6.2.2.1. 权限分组 6.3. 密码加密 6.3.1. 什么是散列加密? 6.3.2. 为散列加点儿盐 6.3.3. 散列和认证 III. web应用安全 7. 安全过滤器链 7.1. DelegatingFilterProxy 7.2. FilterChainProxy 7.2.1. 绕过过滤器链 7.3. 过滤器顺序 7.4. 使用其他过滤器 —— 基于框架 8. 核心安全过滤器 8.1. FilterSecurityInterceptor 8.2. ExceptionTranslationFilter 8.2.1. AuthenticationEntryPoint 8.2.2. AccessDeniedHandler 8.3. SecurityContextPersistenceFilter 8.3.1. SecurityContextRepository 8.4. UsernamePasswordAuthenticationFilter 8.4.1. 认证成功和失败的应用流程 9. Basic(基本)和Digest(摘要)验证 9.1. BasicAuthenticationFilter 9.1.1. 配置 9.2. DigestAuthenticationFilter 9.2.1. Configuration 10. Remember-Me认证 10.1. 概述 10.2. 简单基于散列标记的方法 10.3. 持久化标记方法 10.4. Remember-Me接口和实现 10.4.1. TokenBasedRememberMeServices 10.4.2. PersistentTokenBasedRememberMeServices 11. 会话管理 11.1. SessionManagementFilter 11.2. SessionAuthenticationStrategy 11.3. 同步会话 12. 匿名认证 12.1. 概述 12.2. 配置 12.3. AuthenticationTrustResolver IV. 授权 13. 验证架构 13.1. 验证 13.2. 处理预调用 13.2.1. AccessDecisionManager 13.2.2. 基于投票的AccessDecisionManager实现 13.2.2.1. RoleVoter 13.2.2.2. AuthenticatedVoter 13.2.2.3. Custom Voters 13.3. 处理后决定 14. 安全对象实现 14.1. AOP联盟 (MethodInvocation) 安全拦截器 14.1.1. 精确的 MethodSecurityIterceptor 配置 14.2. AspectJ (JoinPoint) 安全拦截器 15. 基于表达式的权限控制 15.1. 概述 15.1.1. 常用内建表达式 15.2. Web 安全表达式 15.3. 方法安全表达式 15.3.1. @Pre 和 @Post 注解 15.3.1.1. 访问控制使用 @PreAuthorize 和 @PostAuthorize 15.3.1.2. 过滤使用 @PreFilter 和 @PostFilter 16. acegi到spring security的转换方式 16.1. Spring Security是什么 16.2. 目标 16.3. 步骤 16.4. 总结 V. 高级话题 17. 领域对象安全(ACLs) 17.1. 概述 17.2. 关键概念 17.3. 开始 18. 预认证场景 18.1. 预认证框架类 18.1.1. AbstractPreAuthenticatedProcessingFilter 18.1.2. AbstractPreAuthenticatedAuthenticationDetailsSource 18.1.2.1. J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource 18.1.3. PreAuthenticatedAuthenticationProvider 18.1.4. Http403ForbiddenEntryPoint 18.2. 具体实现 18.2.1. 请求头认证(Siteminder) 18.2.1.1. Siteminder示例配置 18.2.2. J2EE容器认证 19. LDAP认证 19.1. 综述 19.2. 在Spring Security使用LDAP 19.3. 配置LDAP服务器 19.3.1. 使用嵌入测试服务器 19.3.2. 使用绑定认证 19.3.3. 读取授权 19.4. 实现类 19.4.1. LdapAuthenticator实现 19.4.1.1. 常用功能 19.4.1.2. BindAuthenticator 19.4.1.3. PasswordComparisonAuthenticator 19.4.1.4. 活动目录认证 19.4.2. 链接到LDAP服务器 19.4.3. LDAP搜索对象 19.4.3.1. FilterBasedLdapUserSearch 19.4.4. LdapAuthoritiesPopulator 19.4.5. Spring Bean配置 19.4.6. LDAP属性和自定义UserDetails 20. JSP标签库 20.1. 声明Taglib 20.2. authorize 标签 20.3. authentication 标签 20.4. accesscontrollist 标签 21. Java认证和授权服务(JAAS)供应器 21.1. 概述 21.2. 配置 21.2.1. JAAS CallbackHandler 21.2.2. JAAS AuthorityGranter 22. CAS认证 22.1. 概述 22.2. CAS是如何工作的 22.3. 配置CAS客户端 23. X.509认证 23.1. 概述 23.2. 把X.509认证添加到你的web系统中 23.3. 为tomcat配置SSL 24. 替换验证身份 24.1. 概述 24.2. 配置 A. 安全数据库表结构 A.1. User表 A.1.1. 组权限 A.2. 持久登陆(Remember-Me)表 A.3. ACL表 A.3.1. Hypersonic SQL A.3.1.1. PostgreSQL B. 安全命名空间 B.1. Web应用安全 - <http> 元素 B.1.1. <http> 属性 B.1.1.1. servlet-api-provision B.1.1.2. path-type B.1.1.3. lowercase-comparisons B.1.1.4. realm B.1.1.5. entry-point-ref B.1.1.6. access-decision-manager-ref B.1.1.7. access-denied-page B.1.1.8. once-per-request B.1.1.9. create-session B.1.2. <access-denied-handler> B.1.3. <intercept-url> 元素 B.1.3.1. pattern B.1.3.2. method B.1.3.3. access B.1.3.4. requires-channel B.1.3.5. filters B.1.4. <port-mappings> 元素 B.1.5. <form-login> 元素 B.1.5.1. login-page B.1.5.2. login-processing-url B.1.5.3. default-target-url B.1.5.4. always-use-default-target B.1.5.5. authentication-failure-url B.1.5.6. authentication-success-handler-ref B.1.5.7. authentication-failure-handler-ref B.1.6. <http-basic> 元素 B.1.7. <remember-me> 元素 B.1.7.1. data-source-ref B.1.7.2. token-repository-ref B.1.7.3. services-ref B.1.7.4. token-repository-ref B.1.7.5. key 属性 B.1.7.6. token-validity-seconds B.1.7.7. user-service-ref B.1.8. <session-management> 元素 B.1.8.1. session-fixation-protection B.1.9. <concurrent-control> 元素 B.1.9.1. max-sessions 属性 B.1.9.2. expired-url 属性 B.1.9.3. error-if-maximum-exceeded 属性 B.1.9.4. session-registry-alias 和session-registry-ref 属性 B.1.10. <anonymous> 元素 B.1.11. <x509> 元素 B.1.11.1. subject-principal-regex 属性 B.1.11.2. user-service-ref 属性 B.1.12. <openid-login> 元素 B.1.13. <logout> 元素 B.1.13.1. logout-url 属性 B.1.13.2. logout-success-url 属性 B.1.13.3. invalidate-session 属性 B.1.14. <custom-filter> 元素 B.2. 认证服务 B.2.1. <authentication-manager> 元素 B.2.1.1. <authentication-provider>元素 B.2.1.2. 使用 <authentication-provider> 来引用一个 AuthenticationProvider Bean B.3. 方法安全 B.3.1. <global-method-security> 元素 B.3.1.1. secured-annotations 和jsr250-annotations 属性 B.3.1.2. 安全方法使用<protect-pointcut> B.3.1.3. <after-invocation-provider> 元素 B.3.2. LDAP命名空间选项 B.3.2.1. 使用<ldap-server> 元素定义LDAP服务器 B.3.2.2. <ldap-provider> 元素 B.3.2.3. <ldap-user-service> 元素
1. 什么是Spring Security框架? Spring Security框架是一个基于Spring框架的安全框架,用于实现身份验证和访问控制等安全功能。 2. Spring Security的主要功能是什么? Spring Security的主要功能包括身份验证、授权、会话管理、密码管理、安全事件管理和安全日志管理等。 3. Spring Security中的几种认证方式有哪些? Spring Security中的几种认证方式包括基于表单的认证、HTTP Basic认证、HTTP Digest认证、OpenID认证和LDAP认证等。 4. Spring Security中的几种授权方式有哪些? Spring Security中的几种授权方式包括基于角色的访问控制、基于权限的访问控制和基于表达式的访问控制等。 5. Spring Security中的几种过滤器有哪些? Spring Security中的几种过滤器包括UsernamePasswordAuthenticationFilter、BasicAuthenticationFilter、DigestAuthenticationFilter和ConcurrentSessionFilter等。 6. Spring Security中的Session Fixation攻击是什么? Session Fixation攻击是一种攻击方式,攻击者在用户未登录,通过伪造一个会话标识符来创建一个会话,并在用户登录将会话标识符发送给用户,使得用户使用了攻击者的会话。 7. Spring Security中的CSRF攻击是什么? CSRF攻击是一种攻击方式,攻击者通过伪造用户的请求,使得用户在不知情的情况下提交了恶意请求。 8. Spring Security如何防止Session Fixation攻击和CSRF攻击? Spring Security可以通过使用Cookie的HttpOnly和Secure属性来防止Session Fixation攻击,通过使用CsrfToken来防止CSRF攻击。 9. Spring Security中的Remember Me认证是什么? Remember Me认证是一种免密登录的方式,用户在登录选择“记住我”,下次再访问该站点将自动登录。Spring Security中可以使用PersistentTokenBasedRememberMeServices来实现Remember Me认证。 10. Spring Security中的密码加密是什么? Spring Security使用PasswordEncoder来对密码进行加密,常见的加密算法有MD5、SHA和BCrypt等。其中,BCrypt是一种适合密码存储的加密算法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值