欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringSecurity框架 —— 安全校验

程序员文章站 2024-03-19 13:40:10
...

前言:

    随着技术的不断发展和完善,极大的方便了我们的生活和工作。但也存在着很大的安全隐患,例如:黑客的攻击。如果没有一定的安全防护措施 将会带来巨大的 业务损失。安全访问控制 特别是大型企业 都非常重视的一个环节。

1. Spring Security 的入门介绍

    Spring Security 是一个能够为基于 Spring 的企业应用系统提供声明式 的安全访问控制解决方案 的安全框架。它提供了一组可以在 Spring 应用上下文中配置 的 Bean,充分利用了 Spring IoC(控制反转),DI(依赖注入) 和 AOP(面向切面编程) 的功能,为应用系统提供 声明式的安全访问控制功能,减少了为 企业系统安全控制 编写大量重复代码 的工作。   

    Spring Security 是一种 声明式的安全框架 即:一种配置方式的框架。所有的安全框架都是基于 filter 做的,因为 过滤器可以去拦截所有的请求。Spring Security 有十几个 filter 。当然还有一种安全框架 Shiro ,这款在市面上也是用的挺多的,这里只做对 Spring Security 进行介绍。如果不做安全校验,项目中的所有页面都是对外直接公开可访问的,安全隐患 很大。

    Spring Security 共有四种用法:

    ① 不用数据,所有的数据全部写在配置文件里面,这也是官方提供的 demo (下面的案例也采用这种方法);

    ② 使用数据库,但这种方法使用的数据库是固定死的,不够灵活,因要根据 Spring security 默认实现代码设计数据库;

    ③ Spring Security 与 Acegi 有所不同,它不能修改默认的filter,只能添加新的filter;

    ④ 暴力手段 修改源码,这种方法 不可取,不实际。

2. 下面通过 demo 详细介绍 Spring Security 的使用 和 配置信息

2.1 创建一个maven (war) 工程

2.1.1 简单演示

    (1) 添加pom 依赖

<properties>
    <spring.version>4.2.4.RELEASE</spring.version>
</properties>

<dependencies>
    <!-- spring 相关的 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <!-- security 相关的 -->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
        <version>4.1.0.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
        <version>4.1.0.RELEASE</version>
    </dependency>
    <!-- servlet -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
        <scope>provided</scope>
    </dependency>
</dependencies>
<build>
    <plugins>		
        <!-- java编译插件 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.2</version>
            <configuration>
                <source>1.7</source>
                <target>1.7</target>
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>      
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <configuration>
                <!-- 指定端口 -->
                <port>9090</port>
                <!-- 请求路径 -->
                <path>/</path>
            </configuration>
        </plugin>
    </plugins>  
</build>

    (2) web.xml 文件配置

注意:Security 是有十几个filter的,下面的 配置里面 我们使用的是 filterChain 是一个过滤器链,使用这一个即可。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">	

	<!-- 通过监听 加载spring 配置文件 -->
  	 <context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-security.xml</param-value>
	 </context-param>
	 <listener>
		<listener-class>
			org.springframework.web.context.ContextLoaderListener
		</listener-class>
	 </listener>
	
	<!-- 拦截器的固定写法 name不能随便改动,并且springSecurityFilterChain 是一个过滤器链
		springSecurityFilterChain 会在 spring-security.xml 配置文件中创建一个对应的Bean
	-->
	 <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-app>

    (3) security 所需要的 resources目录下的 .xml 的配置文件 spring-security.xml 文件

注意:xmlns 是命名空间,在下面的配置中是默认的,而 xmlns:beans 不是默认的,所以在创建 Bean 的时候 使用的标签是<beans:bean>

    Spring Security 的两个功能:认证 和 授权。认证,说的就是登陆 即:谁在登录;授权,说的是登录后 能做什么,或者能访问哪些页面等,即:登录后 必须拥有相应的角色 做相应的事。

    ①<Http> 标签参数介绍:

        pattern:拦截的路径,如果没有登录就直接拦截,不让访问页面
            /*:只拦截当前目录(webapp)下的文件,当前目录下的文件夹里面文件不能被拦截
            /**:拦截当前目录(webapp)下以及当前目录下文件夹里面的文件。(存在递归)
        access: 登录之后所给的权限,登录成功后 也要有相应的权限才能访问页面
            两种写法:
            ①http中有一个熟悉:use-expressions 默认是true 使用表达式,那么access必须写成hasRole()
            ②use-expressions 改为false 那么access=ROLE_USER

        <form-login>: 如果没有登录页面,此标签会提供一个默认的登录页面,输入地址:localhost:9090

                              SpringSecurity框架 —— 安全校验

     ② 认证管理器 authorities: 指定当前用户 所拥有的权限

<?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:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
						http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
    
    <!-- 拦截规则 -->
    <http use-expressions="true"><!-- 如果是true, use-expressions可以省略不写-->
        <intercept-url pattern="/**" access="hasRole('ROLE_USER')"/>
        <!-- 如果没有写登录页面,form-login 会提供一个默认的登录页面 -->
        <form-login/>
    </http>
    
    <!-- 认证管理器 -->
    <authentication-manager>
        <authentication-provider>
            <user-service>
                <!-- 指定哪个用户进行登录 这种方式是写死的,应该是去数据库里查找
                    authorities:指定当前用户 有哪些权限
                -->
                <user name="admin" password="admin" authorities="ROLE_USER"/>
            </user-service>
        </authentication-provider>
    </authentication-manager>
		
</beans:beans>

    (4) 创建 页面 index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
欢迎来到神奇的 Security 世界
</body>
</html>

    (5) 运行 测试  — —  需要注意的情况如下:

    浏览器中输入地址: localhost:9090/index.html ,会被自动拦截跳转到 localhost:9090/login 页面

    SpringSecurity框架 —— 安全校验

    情况一:如果登录失败 就会自动跳转的一个错误页面

     SpringSecurity框架 —— 安全校验

    情况二:当登录成功后,会报一个错误,说是 找不到 favicon.ico,其实就是一个小图标,例如打开百度网页后,在标题栏上"百度一下,你就知道 "  前面的那个 小 爪印的图标。

     SpringSecurity框架 —— 安全校验

    解决方案: 网上找一个 放到 webapp 目录下面就 OK 了 。

    此时在访问 localhost:9090/index.html 即可成功进入页面

     SpringSecurity框架 —— 安全校验

    情况三:如果 认证管理器中的权限 与 拦截器里面的权限 不一致,会出现 可以登录 但是却无法访问 网页

    例如:access="hasRole('ROLE_USER')"         而    authorities="ROLE_ADMIN"  此时 权限不一致,就会出现下面情况:

    SpringSecurity框架 —— 安全校验

 

 

2.1.2 升级演示

    情 景 一:

    (1) 角色的使用演示 

    ① 修改 spring-security.xml 文件,如下:  三个拦截规则 ,三个用户。演示效果:对应的用户只能访问对应规则下的页面。

    拦截规则修改:三个规则 及对应三个页面

<http><!-- 如果是true, use-expressions可以省略不写-->
    <intercept-url pattern="/index1.html" access="hasRole('ROLE_USER1')"/>
    <intercept-url pattern="/index2.html" access="hasRole('ROLE_USER2')"/>
    <intercept-url pattern="/index3.html" access="hasRole('ROLE_USER3')"/>
		
    <!-- 如果没有写登录页面,form-login 会提供一个默认的登录页面 -->
    <form-login/>
</http>

    ② 认证管理器修改:三个 用户 对应三个 规则

<!-- 认证管理器 -->
<authentication-manager>
    <authentication-provider>
        <user-service>
            <!-- 指定哪个用户进行登录 这种方式是写死的,应该是去数据库里查找
                    authorities:指定当前用户 有哪些权限
            -->
            <user name="admin1" password="admin" authorities="ROLE_USER1"/>
            <user name="admin2" password="admin" authorities="ROLE_USER2"/>
            <user name="admin3" password="admin" authorities="ROLE_USER3"/>
        </user-service>
    </authentication-provider>
</authentication-manager>

    ③ 修改 web.xml 配置 index1.xml,index2.xml,index3.xml 这三个页面,否则在访问 localhost:9093 的时候会报错

    SpringSecurity框架 —— 安全校验

    ④ 运行 测试 ,如果是上次已登录 可输入 localhost:9093/login 即:退出上次登录,重新进行登录

    SpringSecurity框架 —— 安全校验

    登录后的显示结果:

    SpringSecurity框架 —— 安全校验

    如果现在输入地址:localhost:9093/index2.html  或者 localhost:9093/index3.html 是拒绝访问的,因为登录时是使用 admin1 进行的登录,没有访问 index2.html 和 index3.html 的权限。使用对应的权限才能访问对应的页面,上面的配置信息

     SpringSecurity框架 —— 安全校验

 

   情 景 二:

    (2) <form-login/> 登录页面的完善

    ① 创建一个登录页面 login.html:

        需要注意的是,① action 必须是 "/login" ;② method 必须是 "post" ;③ 用户名的 name 必须是 "usrename"

        因为 <form-login/> 默认的登录就是 localhost:端口号/login。

    SpringSecurity框架 —— 安全校验

   ② 修改 spring-security.xml 文件 给 <form-login/> 标签 指定自定义的登录页面

           注意:"/login.html" 里面的 "/" 不能省略,它指向的是 项目路径

<form-login login-page="/login.html"/>

    ③ 启动服务,运行测试   地址栏: localhost:9093

    SpringSecurity框架 —— 安全校验

 

   情 景 三:

    (3) 在上面的配置( spring-security.xml )中,因为我们只拦截了三个页面,实际业务开发中 这是不安全的,通常都是要把 "/**" 这个拦截级别添加上的,但是问题来了,如果 添加 "/**" 这个拦截级别 那么 login.html 这个自定义登录页 也访问不了了,这时候还需要一个 匿名访问配置(不进行校验拦截,使 /** 无效的配置 )。

    ① 追加 "/**" 拦截级别: <intercept-url pattern="/**" access="hasRole('ROLE_USER1')"/>

<!-- 拦截规则 -->
<http use-expressions="true">
    <intercept-url pattern="/**" access="hasRole('ROLE_USER1')"/>
    <intercept-url pattern="/index1.html" access="hasRole('ROLE_USER1')"/>
    <intercept-url pattern="/index2.html" access="hasRole('ROLE_USER2')"/>
    <intercept-url pattern="/index3.html" access="hasRole('ROLE_USER3')"/>
		
    <!-- 如果没有写登录页面,form-login 会提供一个默认的登录页面 
        login-page: 指定自定义的登录页面
    -->
    <form-login login-page="/login.html"/>
</http>

    ② 添加 匿名访问 : 不进行校验拦截,

<!-- 匿名访问 -->
<http pattern="/login.html" security="none"></http>

    ③ 运行 测试 

    SpringSecurity框架 —— 安全校验

    但是有个问题,当点击登录的时候 会报下面的一个错误,

    HTTP Status 403 - Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.

    这个错误不是 访问拦截的错误,它是说 在你的 session 里面 没有找到 CSRF token 

SpringSecurity框架 —— 安全校验

    那么 问题来了,什么是 CSRF 呢? 它是 跨站请求伪造 即:一种 黑客的攻击手段。

跨站请求伪造 简介:
    当我们通过自己的电脑 去访问自己的银行账户,这时候肯定要去连接银行的系统;那么如果在与银行系统保持连接的同时,我们又去访问了其他的页面,如果我们所访问的其他的页面里面包含一些攻击的代码,这些攻击代码就会模拟你的请求 继续向银行发送请求,模拟发送的这些请求如果只是简单的查询请求(get请求)是没问题的,如果进行一些操作修改(通常指post请求),此时银行系统是可以接受并处理的,因为银行系统以为是你本人在操作。这就是 跨站请求伪造。
解决跨站请求伪造攻击的方式:
    方式一:短信验证码
    攻击代码 在模拟你的请求的时候 是不知道你的验证码的。而你本人在发送请求的时候都是带着验证码的,银行系统就会根据你的验证码判断你的请求是否有效。但是如果每次请求都要发送一下验证码,使用效率也就大打折扣了,这种方式也比较少用。
    方式二:security 的解决方案
    security 会在你的页面上 得到一个 CSRF 的 token ,所谓的 token 就是令牌 即 随机字符串(本质还是验证码),这个验证码可以在你的服务器中生成 ,放到你的 session 里面,当你进行登录的时候 就可以把从页面得到的这个验证码 提交到后台 并进行校验。(目前我们的demo 仅仅是 html 的操作,服务器生成并页面提交这款可自行操作)通常是 jsp 用这个 token 会很方便(获取 session 数据时)。
    此处我们 禁用 CSRF 即可。 在配置文件 spring-security.xml 中的 http 标签里面添加 <csrf disabled="true">
    此时在进行登录,也就不会再出现上面的错误:

<csrf disabled="true"/>

    ④ 当我们在第三步中 添加了 自定义的登录页 后 ,我们不难发现,我们在使用 默认的登录页的时候 并没有进行CSRF 的校验检查,那这是为什么呢?

    因为:CSRF 主要针对的就 post 请求,当我们使用默认的登录页的时候,它是自己的 from 表单 向自己提交,所以不需要进行校验;当使用自定义的 登录页去登录的时候 ,security 就认为这是 外部网站去访问的,当外部网站去访问的时候,并且还是 post 请求,这时候就需要去校验了。

 

 

   情 景 四 :

    (4) 当我们访问 index1.html 的时候,输入地址 :localhost:9093/index1.html 会被拦截到登录页面 :localhost:9093/login ,但是当我们登录成功之后 又会自动跳转到 localhost:9093/index1.html 页面。

    那么问题来了,如何跳转到我们自己的指定的页面呢?

    那就需要在 <form-login/> 标签里面指定两个属性:

        ① authentication-success-forward-url  :指定登录成功之后 要跳转到的页面

        ② always-use-default-target :与 authentication-success-forward-url 配合使用,只有登录成功,就跳转到 ① 指定的路径。

    对 spring-security.xml 的修改:都在<http> 标签中修改

   添加 如下 配置信息:

<intercept-url pattern="/success.html" access="hasRole('ROLE_USER3')"/>
		
<!-- 如果没有写登录页面,form-login 会提供一个默认的登录页面 
    login-page: 指定自定义的登录页面
-->
<form-login login-page="/login.html" 
    authentication-success-forward-url="/success.html"
    always-use-default-target="true"/>

    

 

    情 景 五 :

    (5) 有登录页面,当然也有退出后的页面,在 <http> 标签里面,添加 <logout/> 默认的是当成功退出后 直接跳转到登录页面( login.html ) 。

    当然 他也可以指定退出后的自定义页面

    属性:logout-success-url : 成功登出后 跳转到的页面

<logout logout-success-url="logout.html"/>

 

 

总结:Spring-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:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
						http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">

	<!-- 匿名访问 -->
	<http pattern="/login.html" security="none"></http>
	
	<!-- 拦截规则 -->
	<http use-expressions="true">
		<intercept-url pattern="/**" access="hasRole('ROLE_USER1')"/>
		<intercept-url pattern="/index1.html" access="hasRole('ROLE_USER1')"/>
		<intercept-url pattern="/index2.html" access="hasRole('ROLE_USER2')"/>
		<intercept-url pattern="/index3.html" access="hasRole('ROLE_USER3')"/>
		<intercept-url pattern="/success.html" access="hasRole('ROLE_USER1')"/>
		<intercept-url pattern="/logout.html" access="hasRole('ROLE_USER1')"/>
		
		<!-- 如果没有写登录页面,form-login 会提供一个默认的登录页面 
			login-page: 指定自定义的登录页面
		 -->
		<form-login login-page="/login.html" 
			authentication-success-forward-url="/success.html"
			always-use-default-target="true"/>
		<csrf disabled="true"/>
		<logout logout-success-url="logout.html"/>
	</http>
	
	<!-- 认证管理器 -->
	<authentication-manager>
		<authentication-provider>
			<user-service>
				<!-- 指定哪个用户进行登录 这种方式是写死的,应该是去数据库里查找
					authorities:指定当前用户 有哪些权限
				-->
				<!-- <user name="admin" password="admin" authorities="ROLE_USER"/> -->
				
				<user name="admin1" password="admin" authorities="ROLE_USER1"/>
				<user name="admin2" password="admin" authorities="ROLE_USER2"/>
				<user name="admin3" password="admin" authorities="ROLE_USER3"/>
				
			</user-service>
		</authentication-provider>
	</authentication-manager>
</beans:beans>

 

 

 

相关标签: SpringSecurity