`
myCsdn_taoge
  • 浏览: 38712 次
  • 性别: Icon_minigender_1
  • 来自: 沈阳
文章分类
社区版块
存档分类
最新评论

SpringSecurity工作原理小解读

 
阅读更多
  •   SecurityContextPersistenceFilter
  •   ConcurrentSessionFilter
  •   WebAsyncManagerIntegrationFilter
  •   HeaderWriterFilter
  •   CsrfFilter
  •   LogoutFilter
  •   UsernamePasswordAuthenticationFilter
  •   DefaultLoginPageGeneratingFilter
  •   RequestCacheAwareFilter
  •   SecurityContextHolderAwareRequestFilter
  •   RememberMeAuthenticationFilter
  •   AnonymousAuthenticationFilter
  •   SessionManagementFilter
  •   ExceptionTranslationFilter
  •   FilterSecurityInterceptor

 

如果要在我们自己的项目中整合SpringSecurity,,应该实现SpringSecurity里面的UserDetailsService,并且将USer权限authorities.add(new SimpleGrantedAuthority(role.getId()));将权限存放到实体的当中,同时User实体要实现UserDetails这个接口,这个接口里面的内容为:

package org.springframework.security.core.userdetails;

import java.io.Serializable;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;

public abstract interface UserDetails
  extends Serializable
{
  public abstract Collection<? extends GrantedAuthority> getAuthorities();//这个是权限相关
  
  public abstract String getPassword();
  
  public abstract String getUsername();
  
  public abstract boolean isAccountNonExpired();
  
  public abstract boolean isAccountNonLocked();
  
  public abstract boolean isCredentialsNonExpired();
  
  public abstract boolean isEnabled();
}

 所在在User里获取相关权限代码可以这样写:

Collection authorities = new ArrayList();

   

     List<Role> roles = user.getRoles();//获取该用户所拥有的权限

      for (Role role : roles) {

       authorities.add(new SimpleGrantedAuthority(role.getId()));

      }

 

   user.setAuthorities(authorities);

 

下面是SpringSecurity里面的实体User类:

package org.springframework.security.core.userdetails;

import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.Assert;

public class User
  implements UserDetails, CredentialsContainer
{
  private static final long serialVersionUID = 320L;
  private String password;
  private final String username;
  private final Set<GrantedAuthority> authorities;
  private final boolean accountNonExpired;
  private final boolean accountNonLocked;
  private final boolean credentialsNonExpired;
  private final boolean enabled;

  public User(String username, String password, Collection<? extends GrantedAuthority> authorities)
  {
    this(username, password, true, true, true, true, authorities);
  }

  public User(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities)
  {
    if ((username == null) || ("".equals(username)) || (password == null)) {
      throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
    }

    this.username = username;
    this.password = password;
    this.enabled = enabled;
    this.accountNonExpired = accountNonExpired;
    this.credentialsNonExpired = credentialsNonExpired;
    this.accountNonLocked = accountNonLocked;
    this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
  }

  public Collection<GrantedAuthority> getAuthorities()
  {
    return this.authorities;
  }

  public String getPassword() {
    return this.password;
  }

  public String getUsername() {
    return this.username;
  }

  public boolean isEnabled() {
    return this.enabled;
  }

  public boolean isAccountNonExpired() {
    return this.accountNonExpired;
  }

  public boolean isAccountNonLocked() {
    return this.accountNonLocked;
  }

  public boolean isCredentialsNonExpired() {
    return this.credentialsNonExpired;
  }

  public void eraseCredentials() {
    this.password = null;
  }

  private static SortedSet<GrantedAuthority> sortAuthorities(Collection<? extends GrantedAuthority> authorities) {
    Assert.notNull(authorities, "Cannot pass a null GrantedAuthority collection");

    SortedSet sortedAuthorities = new TreeSet(new User.AuthorityComparator(null));

    for (GrantedAuthority grantedAuthority : authorities) {
      Assert.notNull(grantedAuthority, "GrantedAuthority list cannot contain any null elements");
      sortedAuthorities.add(grantedAuthority);
    }

    return sortedAuthorities;
  }

  public boolean equals(Object rhs)
  {
    if ((rhs instanceof User)) {
      return this.username.equals(((User)rhs).username);
    }
    return false;
  }

  public int hashCode()
  {
    return this.username.hashCode();
  }

  public String toString()
  {
    StringBuilder sb = new StringBuilder();
    sb.append(super.toString()).append(": ");
    sb.append("Username: ").append(this.username).append("; ");
    sb.append("Password: [PROTECTED]; ");
    sb.append("Enabled: ").append(this.enabled).append("; ");
    sb.append("AccountNonExpired: ").append(this.accountNonExpired).append("; ");
    sb.append("credentialsNonExpired: ").append(this.credentialsNonExpired).append("; ");
    sb.append("AccountNonLocked: ").append(this.accountNonLocked).append("; ");
    boolean first;
    if (!this.authorities.isEmpty()) {
      sb.append("Granted Authorities: ");

      first = true;
      for (GrantedAuthority auth : this.authorities) {
        if (!first) {
          sb.append(",");
        }
        first = false;

        sb.append(auth);
      }
    } else {
      sb.append("Not granted any authorities");
    }

    return sb.toString();
  }
}

 

 

上面列的这些就是SS整个的过滤器集合,15个,非常多,其实web开发用到的过滤器没有那么多。简单的说过滤器作用在这里体现的就是 “检验 请求(request) 和 响应 (response)”具体哪些是对开发人员是要熟悉的, 待我们验证完就知道了!

 

那么在SS中是如何工作的呢?

完成基本的配置工作:web.xml 配置SS的相关信息
<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>
 
在web应用中,当浏览器发起一个请求到展现出一个页面的内容就是一个完成的请求和响应:
1、在这里过程中,request 是直接访问的资源URL, 未经过处理就返回了相关数据
2、或者是request 和 response 都经过了相关处理返回相关数据
 
SS就是走的第二种路, 在request和response都经过了一系列的处理。那么要处理这些请求和响应最好的方法就是Servlet过滤器了。至于处理了什么? 怎么处理的?我们再看!
 
SS 作为一种安全框架, 我们暂且先讨论的是web部分的安全问题,因为SS还可以给其他的客户端做安全处理。大概一个请求响应如下步骤:
1:浏览器的请求
2:过滤器拦截URL
3:认证授权
4:访问授权
 
一步步具体对这四个步骤进行解读
 
1、浏览器的请求
       发起一个标准的http协议的请求URL “http://www.access.com/resource/accountlist";
       上面的一个URL地址就是标准的HTTP协议的请求:协议头, 域名主体,端口80, 资源位置
 
2、过滤器拦截URL
     这里就涉及到了过滤器了,很想知道的是从请求发起的URL被什么过滤器拦截了?
      首页在SS中注册的过滤器有15种,即文章开头所列出来的,这15个过滤器分别被注入到了SS的过滤器链中,由Spring的IOC容器来管理。
      并且如果我们自己自定义的过滤器,实现了其上的任意中一种,那么也一样会注入到SS的过滤器链中被维护起来。
      所有的URL 都会经过这15个过滤器,每个过滤器执行不同的职责(比如:认证,授权,访问,内置对象的初始化等等)
      进入到这个过滤器的前提是被请求的URL地址是要需要经过SS的,那么如何让URL经过这些过滤器的拦截的呢?
 
      在Security.xml 配置文件中
 
         <http pattern="/resources/**" security="none"/>
 
        配置中所描述的就是以/resource/**  形式的URL 是不需要经过过滤器的处理的,其余的那么都会经过拦截器的处理。
        在这里大概阐述下这些拦截器做了什么事情,只列出web方面要比较常用的。
 
过滤器
描述
SecurityContextPersistenceFilter 
存储每一个请求来的上下文对象,在身份验证之前调用
LogoutFilter 
系统退出
UsernamePasswordAuthenticationFilter
执行系统登录的过滤器,用来验证用户名密码
DefaultLoginPageGeneratingFilter 
生成SS提供默认的登陆页面
SecurityContextHolderAwareRequestFilter 
对容器的servlet进行包装,重新请求
AnonymousAuthenticationFilter 
对SS的任何访问都要进行认证资源的初始化
FilterSecurityInterceptor
经过SS验证后访问认证地址前的拦截器
 
3、认证授权
       走到这里就很容易能想到,一个URL 要访问受保护的资源,那么可以反过来说这个URL 是要被许可,被认证的。那么在SS 中以用户                       登录成功为准则,保存用户登录信息到SS中作为凭证,再是对要访问的资源进行校验来做到认证的可靠性。
 
        SS中默认的授权方式是在SecurityContextPersistenceFilter过滤器拦截后创建系统认证对象,并传递到AnonymousAuthenticationFilter 过滤器进行认证对象数据的初始化值。到了这一步就相当于系统已经有了一个认证授权的对象。
       
  4、访问授权
        经过认证授权后,请求带着认真对象一并到了访问授权这步骤 AccessDecisionManager 决定如何对URL进行访问处理,FilterInvocationSecurityMetadataSource 已经存储了该URL对应的默认的权限资源。
 如果请求的URL 是一个登陆处理 “/login” 
在访问决定策略器决定处理期之前,会被UsernamePasswordAuthenticationFilter 拦截下来,看名字就知道是用户名和密码的认证过滤器
在过滤器中进行用户名和密码的验证,并把改用户名和密码组成的token存入认证对象AuthenticationManager管理器中。AuthenticationManager随后调用
this.getAuthenticationManager().authenticate(token);
意思是让认证管理器的认证方法去校验,即调用了UserDetail方法,查找权限,并返回一个User对象(SS自带的User对象)
同时在SS上下文中会存储这个认证后的对象,再进行AccessDecisionManager 的决定认证,首先会把认证用户的权限给取出来,然后再系统中预加载的URL对应的权限集合加载出来一一比对,如果符合则放行,且上下文中已经是认证有效的。可以通过SS的一些JSP标签获取认证后的用户信息。
 
如果请求的URL是一个非登陆处理的 “/XXX"
不像登陆处理的先被UsernamePasswordAuthenticationFilter 拦截, 此时经过这条拦截器不做任何处理,到了访问决策处理器的处理。改处理会把该URL所对应的权限集合加载出来,然后已预加载的认证对象里的认证资源(权限,资源)进行比对,当比对不成功就表示访问是失败的,则抛出访问异常,这些访问异常会被抽象类的调用者AbstractSecurityInterceptor 这个拦截器所捕捉。进行一个异常的处理,这种异常SS当做是认证异常,那么会从SS系统预留的URL中加载出登录页面的URL,如果配置的是/xxx.jsp, 那么就会跳转到/xxx.jsp登陆页面要求输入用户名和密码去校验。
提交表单到过滤器,就执行了登陆处理的“/login”和 登陆处理的流程是一样的。
 
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics