spring与struts的区别-security2和3的区别

《Spring Security 3》 【第三章】增强用户体验(2) - CSDN博客
《Spring Security 3》 【第三章】增强用户体验(2)
摘要: 术语退出(Logout)指的是用户使其安全session失效的一种操作。一般来说,用户在退出后,将会被重定向到站点的非安全保护的界面。让我们在站点的页头部分添加一个“Log Out”的链接,并再次访问站点以了解其如何实现功能的。
理解退出功能
术语退出(Logout)指的是用户使其安全session失效的一种操作。一般来说,用户在退出后,将会被重定向到站点的非安全保护的界面。让我们在站点的页头部分添加一个“Log Out”的链接,并再次访问站点以了解其如何实现功能的。
在站点页头上添加“Log Out”链接
正如我们在第二章中讨论的那样,Spring Security将会监视一些特殊的URL,这些URL将会触发过滤器链中的一个或多个过滤器。用户实现退出的URL标识为/j_spring_security_logout。在header.jsp中添加一个退出的链接与为一个锚标签(即a标签)添加适合的href属性:
value=&/j_spring_security_logout&&var=&logoutUrl&/&&&&
href=&${logoutUrl}&&Log
Out&/a&&/li&
如果你重新加载站点并点击“Log Out”链接,你会发现被重置到登录form的界面。现在看来,登录和退出是很有趣的!
【使用JSTL URL标签来处理相对URL。我们使用了JSTL核心库中的url标签来保证提供的URL在部署的web应用中能够被正确处理。url标签将提供的URL按照相对路径(以/开始)进行处理。你可能会见过其他类似的实现技术如使用JSP的代码((&%= request.getContextPath() %&)),但是JSTL的url标签能够使得你免于编写内联的代码。】
让我们看一下在这个简单操作的背后都发生了什么。
退出是怎么实现的
当我们点击退出链接时,到底发生了什么?
需要记住的一点是任何URL请求在被servlet处理之前,都会经过Spring Security的过滤器链。所以,/j_spring_security_logout这个URL请求并非对应系统中的一个JSP,也不必有一个真正的JSP或者Spring MVC的目标来对其进行处理。这种类型的URL通常被称为虚拟URL。
请求/j_spring_security_logout的URL被o.s.s.web.authentication.logout.LogoutFilter过滤器所拦截。在Spring Security的众多默认过滤器中,LogoutFilter专门匹配这个虚拟URL并执行相应的操作。
让我们快速地查看一下Spring Security的security命名空间提供的默认退出功能:
auto-config=&true&&use-expressions=&true&&&&&
invalidate-session=&true&
&&&&&&logout-success-url=&/&
&&&&&&logout-url=&/j_spring_security_logout&/&&&&
基于这个基本配置,系统将会寻找在logout-url属性配置的URL并实现用户的退出。使得用户退出系统将会涉及如下的三个步骤:
以下的图片阐述了退出的过程:
o.s.s.web.authentication.logout.LogoutHandler接口的实现类可以在用户通过LogoutFilter退出时被调用。你可以实现自己的LogoutHandler(尽管比较复杂)并将其关联到LogoutFilter的生命周期中。通过LogoutFilter默认设置的LogoutHandler将会清除session以及remember me相关的功能,所以用户的session中不会再持有认证相关的信息。最后,通过一个o.s.s.web.
authentication.logout.LogoutSuccessHandler接口的默认实现,页面得以重定向到一个URL。默认实现中会将页面重定向到我们配置的成功退出URL地址(默认为/),但是我们自定义任何系统在用户退出时想要的操作。值得注意的是,退出的处理不应该抛出异常,因为很重要的一点是要在用户的安全session中避免可能出现的潜在不一致性。所以在实现自己的安全处理时要保证异常被正确的处理和记录。
修改logout URL
让我们尝试重写默认的logout URL来提供一个修改自动设置的简单例子。我们将会修改logout URL为/logout。
修改dogstore-security.xml配置文件来包含&logout&元素如下的代码所示:
修改/common/header.jsp文件来改变logout链接的herf属性以匹配新的URL:
重新启动应用并进行尝试。你可以发现使用/logout URL取代了/j_spring_security_logout实现用户的退出。你可能也会发现当你尝试/j_spring_security_logout这个地址时,你会得到一个Page not Found(404)的错误,是因为这个URL不与任何一个实际的servlet资源相对应并且不会被过滤器所处理。
Logout配置命令
&logout&元素包含其他的配置指令以实现更复杂的退出功能,介绍如下:
invalidate-session
如果被设置为true,用户的HTTP session将会在退出时被失效。在一些场景下,这是必要的(如用户拥有一个购物车时)
logout-success-url
用户在退出后将要被重定向到的URL。默认为/。将会通过HttpServletResponse.redirect来处理。
logout-url
LogoutFilter要读取的URL(在例子中,我们改变了它的设置)。
success-handler-ref
对一个LogoutSuccessHandler实现的引用。
感谢 &iteye &lengyun3566 。
他的博客地址:
他的新浪微博:/
本书源代码的地址:/support?nid=4435
本文链接:,转自人气:2149175
访问用户量:3120
笔记经验:3830
总积分:261644
级别:VIP5
搜索本笔记
ta的交流分类
ta的全部笔记
Spring Security 3笔记
交流分类:
附录:参考材料 在本附录中,将会涉及到一些我们感觉有用的参考材料(并相当缺乏文档),而将其插入到章节的内容中又会觉得过于综合。 JBCP Pets示例代码起步 就像我们在第一章:一个不安全应用的剖析中所描述的那样,我们假设你已有了Eclipse 3.4(或3.5)IDE,并包含Web Tools P……
浏览(10060)| (4)|笔记分类
交流分类:
第十三章 迁移到Spring Security 3 在最后一章中,我们将会了解从Spring Security2迁移到Spring Security3时常见问题的相关情况。 在本章中,我们将会:
了解Spring Security 3的重要增强;
理解已有的Spring Security……
浏览(5144)| (0)|笔记分类
交流分类:
第十二章 Spring Security扩展 在本章中,我们将会探索一个Spring Security扩展项目的功能——这是很令人兴奋的功能即将Windows Active Directory认证(或其它支持Kerberos的设施)与Spring Security集成以为你的Intranet用户提供……
浏览(5604)| (0)|笔记分类
交流分类:
在Spring Security中配置客户端证书认证 不同于我们到目前为止所使用的认证机制,使用客户端证书认证会使得用户的请求已经被服务器预先认证(pre-authenticated)了。因为服务器(Tomcat)已经确定用户提供了合法且可信的证书,所以Spring Security只需信任这个as……
浏览(4822)| (0)|笔记分类
交流分类:
第十一章 客户端证书认证(Client Certificate Authentication)
尽管用户名和密码认证特别常见,就像我们在第一章:一个不安全应用的剖析和第二章:Spring Security起步所讨论的那样,form认证的存在允许用户提供各种类型的凭证。Spring Securit……
浏览(4587)| (0)|笔记分类
交流分类:
高级CAS配置 CAS认证框架提供了高级的配置和与CAS服务的数据交换。在本节中,我们将会介绍CAS集成的高级配置。在我们觉得重要的地方将会包含相关的CAS配置指令,但是要记住的是CAS配置是很复杂的并超出了本书的范围。
assertion中获取属性 在CAS服务器传递ticket校……
浏览(7794)| (0)|笔记分类
交流分类:
使用中心认证服务(CAS)进行单点登录
在本章中,我们将会介绍使用中心认证服务(Central Authentication Service,CAS)为基于Spring Security的应用提供单点登录门户(single sign-on portal)。 在本章的内容中,我们将会……
浏览(7689)| (0)|笔记分类
交流分类:
明确的LDAP bean配置 在本节中,我们带领你学会明确配置以下两项功能所需要的bean集合,即连接外部的LDAP服务器和支持授权的LdapAuthenticationProvider。正如其它基于bean的配置,你可能不希望这样做,除非你发现security命名空间风格的配置不能支持你的业务或技……
浏览(3178)| (0)|笔记分类
交流分类:
LDAP的高级配置 一旦我们要了解LDAP基础集成之外的知识,就会发现security XML命名空间方式的配置中,Spring Security LDAP模块还有许多的可用配置。它包括查询用户的个人信息、用户认证的其它方式以及使用LDAP作为UserDetailsService且与DaoAut……
浏览(4151)| (0)|笔记分类
交流分类:
LDAP目录服务
在本章中,我们将会了解轻量级目录访问协议(Lightweight Directory Access Protocol,LDAP)以及它怎样集成到使用Spring Security的应用中以提供认证、授权和用户信息服务。 在本章的内容中,我们将会:
学习一些LDA……
浏览(5403)| (0)|笔记分类
交流分类:
属性 交换(Attribute Exchange) OpenID另外一个有趣的功能就是如果使用OpenID的站点需要,OpenID provider提供(基于用户的许可)典型的用户注册数据,如名字、e-mail、生日。这个功能叫做属性 交换(Attribute Exchange,AX)。下图展现……
浏览(3717)| (0)|笔记分类
交流分类:
OpenID用户的注册问题 请使用我们前面的技术来测试Yahoo! OpenID——例如,/pmularien。你会发现它并不好用,像其它OpenID提供者那样。这带出了OpenID结构的一个很重要的问题,并体现出了启用OpenID用户注册的重要性。
浏览(4691)| (1)|笔记分类
交流分类:
第八章 对OpenID开放
OpenID是很流行的可信任身份管理方式,它允许用户通过一个单独的可信任提供者(provider)管理其身份信息。这个便利的功能为用户提供了安全的方式即使用可信任的OpenID提供者来存储器密码和个人信息,并可以随意的基于请求获取其个人信息。另外,启用OpenID功能……
浏览(5350)| (1)|笔记分类
交流分类:
典型ACL部署所要考虑的事情 实际部署Spring ACL到业务应用是很复杂的。我们总结了Spring ACL要注意的事情,它们在大多数Spring ACL实现场景中都存在。
关于ACL的伸缩性和性能模型
对于小型和中型应用,添加ACL功能是很容易的,尽管它增加数据库存储和影响运行时性能,这……
浏览(4613)| (0)|笔记分类
交流分类:
支持ACL的Spring表达式语言
SpEL对ACL系统的支持仅限于方法安全,通过使用hasPermission SpEL方法。典型情况下,这种类型的访问检查会与引用一个或多个传入参数(进行@PreAuthorize检查)或集合过滤(进行@PostAuthorize检查)联合使用。
遗憾的是……
浏览(4576)| (0)|笔记分类
交流分类:
高级ACL话题 一些高级的话题在我们配置ACL环境时略过了,包括处理ACE许可授权,在运行时根据GrantedAuthority确定某种类型的ACL变化是否允许。既然现在我们已经有了一个运行环境,那我们要开始了解这些更高级的话题。
Permission如何工作 许可授权(permission……
浏览(4629)| (0)|笔记分类
交流分类:
访问控制列表(ACL) 在本章中,我们将会介绍访问控制列表这个复杂话题,它能够提供域对象实例层次授权的丰富模型。Spring Security提供了强大的访问控制列表,但是复杂且缺少文档,它能够很好的满足小到中型规模的实现。 在本章的内容中,我们将会:
理解访问控制列表的概念模型;……
浏览(7396)| (0)|笔记分类
交流分类:
认证事件处理 有一个重要的功能只能通过基于bean的配置就是自定义处理认证事件。认证事件使用了Spring的时间发布机制,它基于o.s.context.ApplicationEvent事件模型。Spring事件模型使用并不广泛,却能够很有用处——特别在认证系统中——如当你想绑定特定行为到认证领域的行……
浏览(5253)| (0)|笔记分类
交流分类:
Spring Security基于bean的高级配置
正如我们在前面几页中看到的那样,基于bean的Spring Security配置尽管比较复杂,但是提供了一定层次的灵活性,如果复杂应用需要超过security XML命名空间风格配置所允许的功能时会用到。
我们将利用这个章节来阐明可用的一……
浏览(4894)| (0)|笔记分类
交流分类:
手动配置Spring Security设施的bean 如果你工作要求的环境很复杂而Spring Security的基本功能——尽管非常强大——不能满足所有的要求,你可能最终需要自己从头构建Spring Security的过滤器链以及支持实施。这是在Spring Security参考手册中没有完全提及……
浏览(4355)| (0)|笔记分类
版权所有 Copyright(C) 私塾在线学习网spring-security3(2)源码分析
&来源:读书人网&【读书人网():综合教育门户网站】
spring-security3(二)源码分析利用断点走了一遍spring-security源码的核心部分,下面根据自己的理解对源码
spring-security3(二)源码分析
利用断点走了一遍spring-security源码的核心部分,下面根据自己的理解对源码做了一些解释,过滤器开头的标号是运行时默认配置调用的顺序,理解了原理,我们可以通过继承和实现接口的方式扩展过滤器,权限验证器,数据查询器,投票器等等......1.SecurityContextPersistenceFilter&&&&&&& 从HttpSession中获取SecurityContext上下文 2.logoutFilter&&&&&&&&&&&&&&&&&&&&&&&&&&& 如果访问地址为/j_spring_security_logout,LogoutFilter将注销用户3.AbstractAuthenticationProcessingFilter& 权限管理器如果访问地址为/j_spring_security_check则选择对应的数据查询器来获取存储的用户相关信息4.BasicAuthenticationFilter5.RequestCacheAwareFilter6.SecurityContextHolderAwareRequestFilter& 7.RememberMeAuthenticationFilter&&&&&&&&& 如果当前SecurityContextHolder中没有用户对象,则通过cookie查找8.AnonymousAuthenticationFilter&&&&&&&&&& 如果当前SecurityContextHolder中没有用户对象,则创建匿名对象9.SessionManagementFilter&&&&&&&&&&&&&&&& 检查session是否超时10.ExceptionTranslationFilter&&&&&&&&&&&& 调用FilterSecurityInterceptor,AbstractSecurityInterceptor使用投票器进行权限判断11.SwitchUserFilter&&&&&&&&&&&&&&&&&&&&&& 用户切换高权限用户向低权限用户切换//从HttpSession中获取SecurityContext上下文 public class SecurityContextPersistenceFilter extends GenericFilterBean {
private SecurityContextRepository repo = new HttpSessionSecurityContextRepository();
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
//代码略.....................
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
SecurityContext contextBeforeChainExecution = repo.loadContext(holder);//获得security上下文
SecurityContextHolder.setContext(contextBeforeChainExecution);
chain.doFilter(holder.getRequest(), holder.getResponse());//调用下一个过滤器
} finally {
SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
// Crucial removal of SecurityContextHolder contents - do this before anything else.
SecurityContextHolder.clearContext();
repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
request.removeAttribute(FILTER_APPLIED);
}//代码略.....................}//如果访问地址为/j_spring_security_logout,LogoutFilter将注销用户public class LogoutFilter extends GenericFilterBean {
private String filterProcessesUrl = "/j_spring_security_logout";
//代码略.....................
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {//判断如果访问地址为/j_spring_security_logout则执行注销,否则跳过
if (requiresLogout(request, response)) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
for (LogoutHandler handler : handlers) {
handler.logout(request, response, auth);
logoutSuccessHandler.onLogoutSuccess(request, response, auth);
chain.doFilter(request, response);
//代码略.....................}//权限管理器如果访问地址为/j_spring_security_check则选择对应的数据查询器来获取存储的用户相关信息public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean implements
ApplicationEventPublisherAware, MessageSourceAware {
//代码略.....................public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {//判断如果访问地址为/j_spring_security_check则跳过进行权限获取和判断,否则执行AnonymousAuthenticationFilter
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response); //调用下一个过滤器BasicAuthenticationFilt
Authentication authR
try {//执行UsernamePasswordAuthenticationFilter类中的方法
authResult = attemptAuthentication(request, response);
if (authResult == null) {
// return immediately as subclass has indicated that it hasn't completed authentication
sessionStrategy.onAuthentication(authResult, request, response);
catch (AuthenticationException failed) {
// Authentication failed
unsuccessfulAuthentication(request, response, failed);
// Authentication success
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
successfulAuthentication(request, response, authResult);}protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,Authentication authResult) throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(authResult);
rememberMeServices.loginSuccess(request, response, authResult); //是否存入cookie
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
successHandler.onAuthenticationSuccess(request, response, authResult);//跳转到目标页面
}//代码略.....................}public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
//代码略.....................
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}//获取页面提交的用户名、密码
String username = obtainUsername(request);
String password = obtainPassword(request);
username = username.trim();//封装成token对象
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
//代码略.....................//调用AbstractAuthenticationManager类中的方法
return this.getAuthenticationManager().authenticate(authRequest);
}//代码略.....................}public abstract class AbstractAuthenticationManager implements AuthenticationManager {//代码略.....................
public final Authentication authenticate(Authentication authRequest) throws AuthenticationException {
return doAuthentication(authRequest);//调用ProviderManager类中的方法
} catch (AuthenticationException e) {
e.setAuthentication(authRequest);
if (clearExtraInformation) {
e.clearExtraInformation();
}//代码略.....................}//权限认证管理器public class ProviderManager extends AbstractAuthenticationManager implements MessageSourceAware, InitializingBean {private List&AuthenticationProvider& providers = Collections.emptyList();private AuthenticationM//代码略.....................
public Authentication doAuthentication(Authentication authentication) throws AuthenticationException {
Class&? extends Authentication& toTest = authentication.getClass();
AuthenticationException lastException =
Authentication result =
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
try {//调用AbstractUserDetailsAuthenticationProvider类中的方法
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
} catch (AccountStatusException e) {
// SEC-546: Avoid polling additional providers if auth failure is due to invalid account status
eventPublisher.publishAuthenticationFailure(e, authentication);
} catch (AuthenticationException e) {
lastException =
if (result == null && parent != null) {
// Allow the parent to try.
result = parent.authenticate(authentication);
} catch (ProviderNotFoundException e) {
// ignore as we will throw below if no other exception occurred prior to calling parent and the parent
// may throw ProviderNotFound even though a provider in the child already handled the request
} catch (AuthenticationException e) {
lastException =
if (result != null) {
if (eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) {
// Authentication is complete. Remove credentials and other secret data from authentication
((CredentialsContainer)result).eraseCredentials();
eventPublisher.publishAuthenticationSuccess(result);
eventPublisher.publishAuthenticationFailure(lastException, authentication);
throw lastE
}//代码略.....................}//权限查询器public abstract class AbstractUserDetailsAuthenticationProvider implements AuthenticationProvider, InitializingBean,
MessageSourceAware {//代码略.....................
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",
"Only UsernamePasswordAuthenticationToken is supported"));
// Determine username
String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
boolean cacheWasUsed =
UserDetails user = this.userCache.getUserFromCache(username); //从Ehcache实现的缓存里取userDetail对象
if (user == null) {
cacheWasUsed =
try {//调用DaoAuthenticationProvider类中的方法
user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
} catch (UsernameNotFoundException notFound) {
logger.debug("User '" + username + "' not found");
if (hideUserNotFoundExceptions) {
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
throw notF
Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");
preAuthenticationChecks.check(user); //检查用户是否有效//通过页面传入的用户名、密码和中取出的信息对比验证
additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
} catch (AuthenticationException exception) {
if (cacheWasUsed) {
// There was a problem, so try again after checking
// we're using latest data (i.e. not from the cache)
cacheWasUsed =
user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
preAuthenticationChecks.check(user);
additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
postAuthenticationChecks.check(user);
if (!cacheWasUsed) {
this.userCache.putUserInCache(user); //将用户对象放入缓存
Object principalToReturn =
if (forcePrincipalAsString) {
principalToReturn = user.getUsername();
return createSuccessAuthentication(principalToReturn, authentication, user);
}//代码略.....................}//查询器class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider{private UserDetailsService userDetailsS//代码略.....................
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
UserDetails loadedU
try {//获取用户信息,可以通过实现UserDetailsService接口或继承JdbcDaoImpl类来自定义内部实现
loadedUser = this.getUserDetailsService().loadUserByUsername(username);//调用自定义类UserDetailsServiceImpl的方法
catch (DataAccessException repositoryProblem) {
throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
if (loadedUser == null) {
throw new AuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
return loadedU
}protected void additionalAuthenticationChecks(UserDetails userDetails,UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
Object salt =
if (this.saltSource != null) {
salt = this.saltSource.getSalt(userDetails);
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"),
includeDetailsObject ? userDetails : null);
String presentedPassword = authentication.getCredentials().toString();//判断密码是否一致
if (!passwordEncoder.isPasswordValid(userDetails.getPassword(), presentedPassword, salt)) {
logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"),
includeDetailsObject ? userDetails : null);
}//代码略.....................}//自定义类实现查询接口public class UserDetailsServiceImpl extends JdbcDaoSupport implements UserDetailsService {private StringauthoritiesByUsernameQprivate StringusersByUsernameQ//代码略.....................public void setAuthoritiesByUsernameQuery(String queryString) {authoritiesByUsernameQuery = queryS}public void setUsersByUsernameQuery(String usersByUsernameQueryString) {this.usersByUsernameQuery = usersByUsernameQueryS}@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
List&UserDetails& users = loadUsersByUsername(username); //访问数据库查询用户信息
if (users.size() == 0) {
logger.debug("Query returned no results for user '" + username + "'");
throw new UsernameNotFoundException(
messages.getMessage("JdbcDaoImpl.notFound", new Object[]{username}, "Username {0} not found"), username);
UserDetails user = users.get(0); // contains no GrantedAuthority[]
Set&GrantedAuthority& dbAuthsSet = new HashSet&GrantedAuthority&();
if (enableAuthorities) {
dbAuthsSet.addAll(loadUserAuthorities(user.getUsername())); //查询用户拥有的角色
if (enableGroups) {
dbAuthsSet.addAll(loadGroupAuthorities(user.getUsername()));
List&GrantedAuthority& dbAuths = new ArrayList&GrantedAuthority&(dbAuthsSet);
addCustomAuthorities(user.getUsername(), dbAuths);
if (dbAuths.size() == 0) {
logger.debug("User '" + username + "' has no authorities and will be treated as 'not found'");
throw new UsernameNotFoundException(
messages.getMessage("JdbcDaoImpl.noAuthority",
new Object[] {username}, "User {0} has no GrantedAuthority"), username);
return createUserDetails(username, user, dbAuths);//返回实现UserDetails接口的对象,将验证信息封装到此对象中}protected List&UserDetails& loadUsersByUsername(String username) {return getJdbcTemplate().query(usersByUsernameQuery, new String[]{username}, new RowMapper&UserDetails&() {@Overridepublic UserDetails mapRow(ResultSet rs, int rowNum) throws SQLException {String username = rs.getString(1);String password = rs.getString(2);boolean enabled = rs.getBoolean(3);return new userDetailsImpl(username, password, enabled);//返回实现UserDetails接口的对象}});}//代码略.....................}//用户信息bean,必须实现UserDetails接口public class userDetailsImpl implements UserDetails {//代码略.....................}//由ExceptionTranslationFilter过滤器调用来进行权限判断public abstract class AbstractSecurityInterceptor implements InitializingBean, ApplicationEventPublisherAware,
MessageSourceAware {//代码略.....................
protected InterceptorStatusToken beforeInvocation(Object object) {//这里读取配置FilterSecurityInterceptor的SecurityMetadataSource属性来获取配置的角色,这些属性配置了资源的安全设置
Collection&ConfigAttribute& attributes = this.obtainSecurityMetadataSource().getAttributes(object);
if (attributes == null) {
if (rejectPublicInvocations) {
throw new IllegalArgumentException("Secure object invocation " + object +
" was denied as public invocations are not allowed via this interceptor. "
+ "This indicates a configuration error because the "
+ "rejectPublicInvocations property is set to 'true'");
publishEvent(new PublicInvocationEvent(object));
// no further work post-invocation
}//这里从SecurityContextHolder中去取Authentication对象,一般在登录时会放到SecurityContextHolder中去
if (SecurityContextHolder.getContext().getAuthentication() == null) {
credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",
"An Authentication object was not found in the SecurityContext"), object, attributes);
}// 如果前面没有处理鉴权,这里需要对鉴权进行处理
Authentication authenticated = authenticateIfRequired();
// Attempt authorization
try {//通过投票器判断当前角色是否有权限访问该地址,如果没有权限则抛出异常,调用AffirmativeBased类中的decide的方法
this.accessDecisionManager.decide(authenticated, object, attributes);
catch (AccessDeniedException accessDeniedException) {//授权不成功向外发布事件
publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
accessDeniedException));
throw accessDeniedE
publishEvent(new AuthorizedEvent(object, attributes, authenticated));
// 这里构建一个RunAsManager来替代当前的Authentication对象,默认情况下使用的是NullRunAsManager会把SecurityContextHolder中的Authentication对象清空
Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
if (runAs == null) {
// no further work post-invocation
return new InterceptorStatusToken(authenticated, false, attributes, object);
SecurityContextHolder.getContext().setAuthentication(runAs);
// need to revert to token.Authenticated post-invocation
return new InterceptorStatusToken(authenticated, true, attributes, object);
}//代码略.....................}//决策器public class AffirmativeBased extends AbstractAccessDecisionManager {//代码略.....................
public void decide(Authentication authentication, Object object, Collection&ConfigAttribute& configAttributes)
throws AccessDeniedException {
int deny = 0;//依次使用各个投票器进行投票,并对投票结果进行计票
for (AccessDecisionVoter voter : getDecisionVoters()) {
int result = voter.vote(authentication, object, configAttributes);
if (logger.isDebugEnabled()) {
logger.debug("Voter: " + voter + ", returned: " + result);
}//这是对投票结果进行处理,如果遇到其中一票通过,那就授权通过,如果是弃权或者反对,那就继续投票
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED: //result:1
case AccessDecisionVoter.ACCESS_DENIED: //result:-1
//这里对反对票进行计数
}//如果有反对票,抛出异常,整个授权不通过
if (deny & 0) {
throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",
"Access is denied"));
//这里对弃权票进行处理,看看是全是弃权票的决定情况,默认是不通过,由allowIfAllAbstainDecisions变量控制
checkAllowIfAllAbstainDecisions();
}//代码略.....................}//角色投票器public class RoleVoter implements AccessDecisionVoter { //代码略.....................
public int vote(Authentication authentication, Object object, Collection&ConfigAttribute& attributes) {
int result = ACCESS_ABSTAIN;
Collection&GrantedAuthority& authorities = extractAuthorities(authentication);
for (ConfigAttribute attribute : attributes) {//这里取得资源的安全配置
if (this.supports(attribute)) {
result = ACCESS_DENIED;
//这里对资源配置的安全授权级别进行判断,也就是匹配ROLE为前缀的角色配置
//遍历每个配置属性,如果其中一个匹配该主体持有的GrantedAuthority,则访问被允许。//当前用户拥有的角色集合,当有任何一个角色满足时授予权限
for (GrantedAuthority authority : authorities) {
if (attribute.getAttribute().equals(authority.getAuthority())) {
return ACCESS_GRANTED;
Collection&GrantedAuthority& extractAuthorities(Authentication authentication) {
return authentication.getAuthorities();
}//代码略.....................}}

我要回帖

更多关于 spring和struts的区别 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信