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默認便可防止會話固定攻擊。