Spring Security学习总结

1.Spring Security介绍 html

        通常来讲,Web 应用的安全性包括用户认证(Authentication)和用户受权(Authorization)两个部分。前端

       用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户可否访问该系统。用户认证通常要求用户提供用户名和密码。系统经过校验用户名和密码来完成认证过程。java

       用户受权指的是验证某个用户是否有权限执行某个操做。在一个系统中,不一样用户所具备的权限是不一样的。好比对一个文件来讲,有的用户只能进行读取,而有的用户能够进行修改。通常来讲,系统会为不一样的用户分配不一样的角色,而每一个角色则对应一系列的权限 。  web

       在认识Spring Security以前,全部的权限验证逻辑都混杂在业务逻辑中,用户的每一个操做之前可能都须要对用户是否有进行该项操做的权限进行判断,来达到认证受权的 目的。相似这样的权限验证逻辑代码被分散在系统的许多地方,难以维护。AOP(Aspect Oriented Programming)和Spring Security为咱们的应用程序很好的解决了此类问题,正如系统日志,事务管理等这些系统级的服务同样,咱们应该将它做为系统一个单独的“切面”进行管 理,以达到业务逻辑与系统级的服务真正分离的目的,Spring Security将系统的安全逻辑从业务中分离出来。且对于上面提到的两种应用情景,Spring Security 框架都有很好的支持。在用户认证方面,Spring Security 框架支持主流的认证方式,包括 HTTP 基本认证、HTTP 表单验证、HTTP 摘要认证、OpenID 和 LDAP 等。在用户受权方面,Spring Security 提供了基于角色的访问控制和访问控制列表(Access Control List,ACL),能够对应用中的领域对象进行细粒度的控制。spring

      spring security是为基于Spring的应用程序提供声明式安全保护的安全性框架。spring security提供了完整的安全性的解决方案,它可以在web请求级别和方法调用级别处理身份验证和受权数据库

2.使用Spring Security       express

   2.1 Spring Security3.0包含8个模块,咱们应用程序至少要包含核心和配置这两个模块apache

模块 描述
ACL 支持经过访问控制列表为域对象提供安全性
CAS客户端 提供与JA-SIG的中心认证服务进行集成的功能
配置 包含了对Spring Security XML命令空间的支持
核心 提供了Spring Security基本库
LDAP 支持基于轻量目录访问协议进行认证
OpenID 支持分散式OpenID标准
Tag Library 包含了一组JSP标签来实现视图级别的安全性
Web 提供了Spring security基于过滤器的web安全性支持

  2.2使用 Spring Security配置命名空间api

       好处:使用命令空间能够将安全性配置从成百行的xml减小到10行。由于不少东西都给你作了数组

          方法一:

<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
    xmlns:security="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/security   
            http://www.springframework.org/schema/security/spring-security-3.0.xsd">  
      
    <!--  Spring-Security 的配置 -->  
    <!-- 注意开启use-expressions.表示开启表达式.  
    see:http://www.family168.com/tutorial/springsecurity3/html/el-access.html  
     -->  
    <security:http auto-config="true" use-expressions="true" access-denied-page="/auth/denied" >  
          
        <security:intercept-url pattern="/auth/login" access="permitAll"/>  
        <security:intercept-url pattern="/main/admin" access="hasRole('ROLE_ADMIN')"/>  
        <security:intercept-url pattern="/main/common" access="hasRole('ROLE_USER')"/>  
          
        <security:form-login  
                login-page="/auth/login"   
                authentication-failure-url="/auth/login?error=true"   
                default-target-url="/main/common"/>  
              
        <security:logout   
                invalidate-session="true"   
                logout-success-url="/auth/login"   
                logout-url="/auth/logout"/>  
      
    </security:http>  
      
    <!-- 指定一个自定义的authentication-manager :customUserDetailsService -->  
    <security:authentication-manager>  
            <security:authentication-provider user-service-ref="userServiceImpl">  
                   <!--  <security:password-encoder ref="passwordEncoder"/>   -->
            </security:authentication-provider>  
    </security:authentication-manager>  
</beans>

           注意在这个配置文件里面不能扫包,就是以下语句

<context:component-scan base-package="com.kedacom.platform.security.service">
</context:component-scan>

       方法二:将安全性的命令空间做为默认的命名空间

<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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.1.xsd">  
           
      
    <!--  Spring-Security 的配置 -->  
    <!--以前就是这个一直没有扫进来,因此建立bean有错误  注意有些可能不带支持这个扫包,就把头给换成如今这个能够扫包的  -->
    <context:component-scan base-package="com.kedacom.service">
</context:component-scan>
    <!-- 注意开启use-expressions.表示开启表达式.  
    see:http://www.family168.com/tutorial/springsecurity3/html/el-access.html  
     -->      
     <!-- auot-config表示会把那7个过滤器的关系自动弄好    access-denied-page 表示没有权限发送这个url-->
     
    <http auto-config="true" use-expressions="true" access-denied-page="/auth/denied" >  
          
        <intercept-url pattern="/auth/login" access="permitAll"/>  
        <intercept-url pattern="/main/admin" access="hasRole('ROLE_ADMIN')"/>  
        <intercept-url pattern="/main/common" access="hasRole('ROLE_USER')"/>  
          
        <!-- 通常咱们不提供登陆页面,他本身会提供登陆页面,全部咱们要指定本身的登陆页面,就在login-page里面指定其访问的url -->
        <!-- authentication-failure-url 表示失败以后去的页面 -->
        <!--  default-target-url  表示成功以后去的页面-->  
        <form-login  
                login-page="/auth/login"   
                authentication-failure-url="/auth/login?error=true"   
                default-target-url="/main/common"/>  
        <!--logout-success-url 表示退出成功以后去的页面  -->
        <!--表示退出要发的url  -->      
        <logout   
                invalidate-session="true"   
                logout-success-url="/auth/login"   
                logout-url="/auth/logout"/>  
      
    </http>  
      
    <!-- 指定一个自定义的authentication-manager :customUserDetailsService -->  
    <authentication-manager>  
            <authentication-provider user-service-ref="userServiceImpl">  
                   <!--  <security:password-encoder ref="passwordEncoder"/>   -->
            </authentication-provider>  
    </authentication-manager>  
</beans:beans>

这种命令空间,就能够避免为每种元素添加那使人讨厌的 "security: " 前缀了,且还能够扫包,省得要去注册本身实现的那个类的bean

 2.3基于内存用户存储进行认证管理

    2.3.1  实例1,spring security的最简单的helloworld程序,用配置文件,将用户、权限、资源(url)硬编码在xml文件中,最简单的spring security的例子来说解spring security怎么使用

           环境:spring mvc3.2.5  ,maven3.2.1,spring security3.1

  1>新建maven项目,加入以下依赖的jar包

                  

            对应的pom.xml文件内容以下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.kedacom.webmvc</groupId>
    <artifactId>securityfirstdemo</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>securityfirstdemo Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <!-- 将jsp编译成servlet,没有的话,jsp会报错 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <!--spring security安全框架 , 进行登陆验证和受权验证的jar包 -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-taglibs</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.0.3</version>
        </dependency>
    </dependencies>
    <properties>
        <spring.security.version>3.1.0.RELEASE</spring.security.version>
        <log4j.version>1.2.12</log4j.version>
    </properties>
    <build>
        <finalName>securityfirstdemo</finalName>
    </build>
</project>

   2>.在web.xml 里面添加spring security过滤器,添加代码以下:        

<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>

咱们使用了DelegatingFilterProxy过滤器, 而后拦截了全部的请求(由于是/*)

  3>在web.xml将spring security的配置加入到spring的上下文中, spring-security的配置文件名为:applicationContext-Security.xml,在web.xml添加的代码以下:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>WEB-INF/applicationContext-Security.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

  4>一个最简单的spring security配置的以下,配置在applicationContext-Security.xml文件中,代码以下:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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.1.xsd">
                        
    <!--auto-config等于true,他会本身给你一个登陆页面,http基本认证和退出功能 -->
    <!--拦截全部的请求,当咱们具有了ROLE_SPITTER角色才能够进行访问 -->
    <http auto-config="true">
        <intercept-url pattern="/**" access="ROLE_SPITTER" />
    </http>
    
    <!--配置内存用户存储 -->
    <!--经过user-service元素来建立一个用户服务 -->
    <user-service id="userService">
        <user name="zhangsan" password="12" authorities="ROLE_SPITTER" />
	<user name="lisi" password="14" authorities="ROLE_AA" />
    </user-service>
    
    <!--将用户服务装配到认证管理器中 -->
    <authentication-manager>
        <authentication-provider user-service-ref="userService"></authentication-provider>
    </authentication-manager>
</beans:beans>

全部的认证和权限控制所有经过配置文件来实现,这里面的内容才是spring security的主要所在。

对上面的配置的稍做讲解:
      a.将auto-config属性配置为true好处,

          咱们能够获得他的免费的一些赠品,配置为true以后sping security会为咱们提供一个额外的登陆页,HTTP基本认证和退出功能,实际上,将auto-config属性配置为true等价于下面这样显示的配置的特性:

<http>
     <form-login/>
     <http-basic/>
     <logout />
     <intercept-url pattern="/**" access="ROLE_SPITTER" />
</http>

      b.<intercept-url>元素

         <intercept-url>元素是实现请求级别的一道防线,他的pattern属性定义了对传入的请求要进行匹配的URL模式,若是请求匹配这个模式的话,<intercept-url>的安全规则就会启用。

         就是表示访问某个请求,必需要某种权限,值得注意的是,在请求匹配上多个的时候,会从下往下进行匹配,知道匹配到第一个匹配的角色,就进行验证。

          假设该应用有一些特定区域,只有管理员才能访问。为了实现这个点,咱们能够在以前的那个条记录前插入以下的<intercept-url>:

 <http auto-config="true">
        <intercept-url pattern="/admin/** access="ROLE_ADMIN"" />
        <intercept-url pattern="/**" access="ROLE_SPITTER" />
    </http>

     若是访问的是 "/admin/a.jsp"请求,其实两个均可以匹配的上,可是从上往下,因为先匹配到第一个,全部必需要"ROLE_ADMIN"角色才能够访问,虽然第二个能够匹配上,可是他再也不往下匹配了。

    c.为了方便期间直接在spring-security里的配置文件里面声明用户详细信息,用户的详细信息声明在<user-service>之中,每一个可以登陆的用户都会有一个<user>元素。属性name和password分别为指定了登陆名和密码,同时authorities属性用于设置逗号分隔的权限列表--即容许用户作的事。从这spring-seccurity的配置中能够看获得,zhangsan能够对全部的URL有权限访问,而lisi没有。

    d.<authentication-manager>元素会注册一个认证管理器。更确切的讲,他将注册一个ProviderManager实例。而后再将用户服务装配进来。

     2.3.2实例二,重写登陆页面, 退出,且根据登陆用户权限的不一样,返回不一样的首页

         在前面的例子中,咱们可能会以为获得了spring security提供的登陆表单是赚了个大便宜,可是这个表单太简单了。且不美观,且咱们改变不了其样式,因此通常咱们会将其替换为本身设计的登陆页面。

      1.重写登陆页面        

        为了设置本身的登陆页面,咱们须要配置<form-login>元素取代默认的行为:   

<form-login login-page="/login" default-target-url="/employee/list"
authentication-failure-url="/login?error=true" />

        登陆页面经过“/login”进行登陆。

        登陆成功后默认url访问/employee/list

        登陆失败后url访问/login?error=true

      2.添加退出,让其退出到本身定义的页面去

<!--退出成功后调转的页面  -->
<logout invalidate-session="true" logout-success-url="/login" />

        让其的session失效。

       注销成功后转向:/login。

     3.更加登陆用户的权限不一样返回不一样的页面:

          在登陆成功以后,去取出当前登陆用户的角色。而后判断其中有什么角色,更加角色来返回不一样的页面,这样,不一样的角色就只可以看到本身对应权限的页面。   

@RequestMapping("/employee")
public class EmployeeController {
    @Autowired
    EmployeeService employeeService;
  
    /*
     * 查看全部的员工的信息
     */
    @RequestMapping("/list")
    public String getallemployee(Model model) {
        List<Employee> lists = employeeService.findall();
        // 注意:用的是字符串,对应对象的形式,就是以下形式
        // model.addAttribute(string arg0, Object arg1)
        model.addAttribute("list", lists);
        //得到当前登陆用户的角色列表
        Set<String> roles = AuthorityUtils.authorityListToSet(SecurityContextHolder.getContext().getAuthentication().getAuthorities());
        if(roles.contains("ROLE_ADMIN")){
            return "home";
        }
        else if(roles.contains("ROLE_USER"))
        {
            return "user/home";
        }
        else{   //这儿有一个 ROLE_ANONYMOUS 系统提供的匿名身份的用户
            return "redirect:/login";
        }
    }
}

      下面这个语句是得到当前登陆用户拥有的权限,是一个数组

SecurityContextHolder.getContext().getAuthentication().getAuthorities()

      下面语句是将当前用户的权限转成集合

AuthorityUtils.authorityListToSet(userAuthorities)

2.4基于数据库的管理用户和角色来使用spring security(企业通常是用这种)

      在上面咱们所讲的两个例子中,咱们的用户信息和权限信息都存在xml中,这是为了演示如何使用最小的配置就可使用spring security。而实际开发中,用户基本信息和权限信息一般都是存储在数据库中,为此下面咱们经过以下实例来讲明企业是怎么使用spring security的。

      实例三,用户信息存储在数据库中,不一样的角色访问的资源不同,管理员对全部的有访问权限,而普通用户只对基本的有访问权限,

       通常会重写系统提供的UserDetailsService接口,而后根据前端传过来的用户名去数据库查询出基本信息根据数据库其权限给其添加权限

        其他都与上面一致就再也不此说明了,只写这个接口的代码:

        
@Service("userServiceImpl")
public class UserServiceImpl implements UserService, UserDetailsService {
    @Autowired
    private UserDao userdao;
    public List<DBUser> userquery(String username) {
        return userdao.userquery(username);
    }
    public UserDetails loadUserByUsername(String username)
        throws UsernameNotFoundException {
        UserDetails deuser = null; 
         try {  
  
             // 搜索数据库以匹配用户登陆名.  
             // 咱们能够经过dao使用JDBC来访问数据库  
             List<DBUser> luser=userdao.userquery(username);
         if(luser == null )
             return deuser;
         DBUser user =  luser.get(0);
         // Populate the Spring User object with details from the dbUser  
         // Here we just pass the username, password, and access level  
         // getAuthorities() will translate the access level to the correct  
         // role type  
        //注意这个user是userDetail包里面的类
           //而dbuser也是userDetail包里面的类
           deuser = new User(user.getUsername(), user.getPassword().toLowerCase(), true, true, true, true,getAuthorities(user.getAccess()));  
         } catch (Exception e) {  
         
             throw new UsernameNotFoundException("Error in retrieving user");  
         }  
        return deuser;
}

    @SuppressWarnings("deprecation")
    public Collection<GrantedAuthority> getAuthorities(Integer access) {  
  
            List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>(2);  
            // 全部的用户默认拥有ROLE_USER权限   
            authList.add(new GrantedAuthorityImpl("ROLE_USER"));  
            // 若是参数access为1.则拥有ROLE_ADMIN权限  
            if (access.compareTo(1) == 0) {         
                authList.add(new GrantedAuthorityImpl("ROLE_ADMIN"));  
            }  
            //返回权限列表,像管理员帐号,zhangsan。返回的authList的值为 :[ROLE_USER, ROLE_ADMIN],而普通用户返回的值为[ROLE_USER]
                return authList;  
     }  
   
}

     这里要注意:这儿咱们从数据库查询出来该用户的基本信息和权限信息以后,将其构建了一个user

deuser = new User(user.getUsername(), user.getPassword().toLowerCase(), true, true, true, true,getAuthorities(user.getAccess()));

       这个user实现了UserDetails接口,主要存储了当前用户的用户名,密码,权限之类的信息,后面当用户访问对应的url的时候,会去这儿取这些数据,而后比较该用户是否拥有这个权限。这些是spring security框架作的事。

3.spring security的工做执行原理

       1)web容器启动加载系统资源和权限列表

       2)用户发出请求

       3)过滤器拦截

       4)取得请求资源所需权限

       5)匹配用户拥有权限和请求权限,若是用户没有权限执行第六步,不然执行第七步

       6)登陆

      7)验证并受权

       其执行工做原理图以下:

    

4.用spring security防止session固化欺骗      

         Session fixation attack(会话固定攻击)是利用服务器的session不变机制,借他人之手得到认证和受权,而后冒充他人。若是应用程序在用户首次访问它时为每一名用户创建一个匿名会话,这时每每就会出现会话固定漏洞。而后,一旦用户登陆,该会话即升级为经过验证的会话。最初,会话令牌并未被赋予任何访问权限,但在用户经过验证后,这个令牌也具备了该用户的访问权限。

 

防止会话固定攻击,能够在用户登陆成功后从新建立一个session id,并将登陆前的匿名会话强制失效。Spring Security默认便可防止会话固定攻击。

相关文章
相关标签/搜索