導語
互聯網已經成爲了我們生活一部分,但是安全問題頻發,身份認證變得更爲複雜。如何讓這一部分更安全?如何讓這一部分的身份驗證變得更簡單?下面將介紹Spring全家桶中的安全框架SpringSecurity快速入門及使用,來解決身份驗證問題.
springsecurity是一個能夠爲基於Spring的企業應用系統提供聲明式的安全訪問控制解決方案的安全框架。它提供了一組可以在Spring應用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反轉Inversion of Control ,DI:Dependency Injection 依賴注入)和AOP(面向切面編程)功能,爲應用系統提供聲明式的安全訪問控制功能,減少了爲企業系統安全控制編寫大量重複代碼的工作.下面使用實現一個springsecurity案例
創建maven工程(war包) 導入pom依賴
<!--Spring和SpringSecurity依賴-->
<dependencies>
<!--springmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.6</version>
</dependency>
<!--SpringSecurity相關依賴-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<!-- MySql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<!--數據連接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.13</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 配置Tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<configuration>
<port>80</port>
<!-- http://127.0.0.1:{port}/{path} -->
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
web.xml配置
<!-- 編碼過濾器 -->
<filter>
<filter-name>characterEncoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--//End 編碼過濾器 -->
<!--springsecurity過濾器,做資源權限的攔截和驗證-->
<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>
<!-- SpringMVC -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
springmvc.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 包掃描 -->
<context:component-scan base-package="com.shemuel.controller" />
<!-- 視圖解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/" />
<property name="suffix" value=".jsp" />
</bean>
<!--引入SpringSecurity配置文件-->
<!--<import resource="spring-security.xml" />-->
<import resource="spring-security.xml"/>
</beans>
springsecurity.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-4.2.xsd ">
<!-- 公共資源取消授權攔截 -->
<!-- <http pattern="/images/**" security="none"/>
<http pattern="/register" security="none"/>
<http pattern="/loginout" security="none"/> -->
<http pattern="/login.html" security="none"/>
<http pattern="/login/fail.html?error" security="none"/>
<!--
auto-config表示自動引入springsecurity相關過濾器
user-expression屬性表示是否使用 表達式
-->
<http auto-config="true" use-expressions="true">
<!--配置需要過濾哪些頁面,所有帶有user的請求都需要ROLE_ADMIN權限-->
<intercept-url pattern="/user/**" access="hasRole('ROLE_ADMIN')" />
<form-login login-page="/login.html"
default-target-url="/user/list.html"
authentication-failure-url="/login/fail.html?error"
username-parameter="username"
password-parameter="password"
always-use-default-target="true" />
<!--禁用CSRF-->
<csrf disabled="true"/>
</http>
<!--授權認證管理器-->
<authentication-manager>
<authentication-provider>
<!-- 引用已經配置的加密算法 -->
<password-encoder ref="encoder"></password-encoder>
<!-- 對指定數據庫表裏的用戶進行授權認證 -->
<jdbc-user-service data-source-ref="dataSource" users-by-username-query="select username, password, enabled from users where username=?" />
</authentication-provider>
</authentication-manager>
<!-- 數據庫連接池 -->
<beans:bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<beans:property name="url" value="jdbc:mysql://127.0.0.1:3306/security?characterEncoding=utf8" />
<beans:property name="username" value="root" />
<beans:property name="password" value="root" />
<beans:property name="driverClassName" value="com.mysql.jdbc.Driver" />
<beans:property name="maxActive" value="10" />
<beans:property name="minIdle" value="5" />
</beans:bean>
<!-- 配置加密算法 -->
<beans:bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="9" /><!-- 加密的密碼長度9 -->
</beans:bean>
</beans:beans>
創建UserController
創建用戶管理的Controller,並加入用戶列表功能
package cn.itcast.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping(value = "/admin/user")
public class UserController {
/***
* 用戶管理
* @return
*/
@RequestMapping(value = "/list")
public String list(){
return "user_list";
}
}
在/WEB-INF/view/下創建user_list.jsp
爲了演示效果,我們這裏整點假數據展示
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>歡迎來到SpringSecurity的世界</title>
</head>
<body>
歡迎來到SpringSecurity的世界!<br />
<table>
<tr>
<td>用戶ID</td>
<td>姓名</td>
<td>年齡</td>
</tr>
<tr>
<td>1</td>
<td>張三</td>
<td>王五</td>
</tr>
<tr>
<td>2</td>
<td>張三2</td>
<td>王五</td>
</tr>
<tr>
<td>3</td>
<td>張三3</td>
<td>王五</td>
</tr>
<tr>
<td>4</td>
<td>張三4</td>
<td>王五</td>
</tr>
</table>
</body>
</html>
實現對/user開始的請求攔截
在spring-security.xml中配置攔截信息和授權認證管理器
<http auto-config="true" use-expressions="true">
<!--所有帶有admin的請求都需要ROLE_ADMIN權限-->
<intercept-url pattern="/user/**" access="hasRole('ROLE_ADMIN')" />
</http>
<!--授權認證管理器-->
<authentication-manager>
</authentication-manager>
http結點主要配置要攔截的url相關權限規則和處理方案。
auto-config =true:默認會配置多個SpringSecurity相關過濾器,如果不配,就不能正常使用SpringSecurity相關功能。
use-expressions:是否使用SpELl表達式。
,pattern表示要攔截的路徑,可以用通配符表示, * * 表示所有路徑。 access表示對應地址訪問所需的權限,如果use-expressions="false"access="hasRole(‘ROLE_ADMIN’)"這裏的hasRole就可以去掉,我們後面都會設置成false,直接去掉這裏的hasRole方便一點。ROLE_ADMIN表示ADMIN角色,這列角色自定義,可以隨意定義什麼角色,不過注意,這裏必須得大寫。
發佈測試
用tomcat發佈測試,端口號根據你本機情況開放,我這裏端口是80,可以省略
訪問 localhost/user/list.html的時候跳轉到了一個登陸界面,說明攔截配置生效了。
添加授權用戶
接着我們爲上面登陸那裏添加授權用於,允許他們登陸。修改spring-security.xml,在authentication-manager結點下加入如下代碼:
<authentication-provider>
<!--硬編碼方式提供賬號密碼-->
<user-service>
<user name="admin" authorities="ROLE_ADMIN" password="123456" disabled="false" />
</user-service>
</authentication-provider>
這裏提供了用戶名爲admin 密碼123456 角色爲ROLE_ADMIN的用戶,這裏的角色必須和上面http裏配置的角色保持一致,否則仍然五權限訪問。disabled=false表示不禁用也就是啓用。這時候我們就可以通過該賬號登錄訪問了。
登錄後我們就可以訪問http://localhost/user/list.html了
Spring Security常用配置
基於上面的案例我們繼續學習SpringSecurity相關知識。
取消安全校驗
我們網站中常常會有一些靜態資源或者不需要校驗權限的地址,例如註冊和登錄,我們在webapp下創建一個images文件夾,在裏面放一張圖片1.png。像這些地址或者靜態資源我們如何取消權限校驗呢?在spring-security.xml中加入如下代碼:
<!--不需要過濾的靜態資源和開放連接-->
<http pattern="/images/**" security="none" />
<http pattern="/login.shtml" security="none" />
<http pattern="/login/fail.shtml" security="none" />
這時候 http://localhost/images/1.jpg 就可以訪問了。
自定義登錄頁面
剛纔那個登錄頁面不太美觀,我們能不能讓登錄地址跳轉到指定頁面呢?
首先我們創建一個登錄頁面
/WEB-INF/view/login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>登錄</title></head>
<body>
歡迎登錄!我自己的登錄頁面,後期可進行美化.
<form name='f' action='/login' method='POST'>
<table>
<tr>
<td>用戶名:</td>
<td><input type='text' name='username' value=''></td>
</tr>
<tr>
<td>密碼:</td>
<td><input type='password' name='password'/></td>
</tr>
<tr>
<td colspan='2'><input name="submit" type="submit" value="Login"/></td>
</tr>
</table>
</form>
</body>
</html>
LoginController
package cn.itcast.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LoginController {
/***
* 登錄
* @return
*/
@RequestMapping(value = "/login")
public String login(){
return "login";
}
}
修改spring-security.xml 在http結點中加入如下代碼:
<!--自定義登錄-->
<form-login login-page="/login.shtml"
default-target-url="/user/list.shtml"
authentication-failure-url="/login/fail.html?error"
username-parameter="username"
password-parameter="password"
always-use-default-target="true" />
- login-page:自定義登錄頁url,默認爲/login
- default-target-url:默認登錄成功後跳轉的url
- authentication-failure-url: 登錄失敗後跳轉的url
- username-parameter:用戶名的請求字段 默認爲userName
- password-parameter:密碼的請求字段 默認爲password
- always-use-default-target:是否始終使用默認的地址,即登錄成功後是否總是跳轉到默認地址
再次登錄,發現報403錯誤(如下),是因爲SpringSecurity這裏做了防csrf攻擊校驗,我們禁用csrf校驗即可。
HTTP Status 403 – Forbidden
Type Status Report
Message Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.
Description The server understood the request but refuses to authorize it.
Apache Tomcat/8.5.16
在http結點加入
<!--禁用CSRF-->
<csrf disabled="true" />
登錄錯誤信息處理
在LoginController中加一個錯誤處理方法
/***
* 登錄失敗
* @return
*/
@RequestMapping(value = "/login/fail")
public String loginfail(@RequestParam(value = "error",required = false)String error, Model model){
if(error!=null){
model.addAttribute("msg","賬號或者密碼不對!");
}
return "login";
}
在登錄的jsp回顯錯誤
<tr>
<td colspan='2'>
${msg}<br />
<input name="submit" type="submit" value="Login"/>
</td>
</tr>
數據庫中的用戶賬號密碼登錄
前面我們一直是寫死的用戶賬號和密碼,而真實環境中基本都是從數據庫獲取賬號密碼,這個如何實現?
首先創建一個數據庫叫springsecurity,接着創建兩張表,一張是users表,存放用戶信息,另一張是authorities表,存放用戶角色信息。
CREATE TABLE `users` (
`username` varchar(50) NOT NULL,
`password` varchar(60) NOT NULL,
`enabled` varchar(50) NOT NULL,
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO users(username,password,enabled)VALUES('itcast','123456','true');
CREATE TABLE `authorities` (
`username` varchar(50) NOT NULL,
`authority` varchar(50) NOT NULL,
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO authorities(username,authority)VALUES('itcast','ROLE_ADMIN');
然後加入在springsecurity.xml中加數據庫連接池配置,上面的最終版配置文件已經有了我這裏就不在寫了. 如果配置了加密,則數據庫裏的密碼也應該爲加密的密碼,否則登錄失敗.
最後登錄成功就能看到如下頁面了
參考:傳智播客教學視頻.
傳智博客官網:http://itcast.cn