安全是每一個項目開發中都須要考慮的,好比權限控制,安全認證,防止漏洞攻擊等。html
比較常見的安全框架有:Apache的shiro、Spring Security等等,相信用shiro的用戶羣體更多,而security功能更多一些。html5
那麼咱們就來看看Spring本身的Security是如何使用的。關於shiro後邊必定會有其餘文章補充的~。java
官方文檔-入門連接web
1)、首先咱們要有一個能夠用來作測試的頁面,否則作了權限控制也不知道有沒有效果,那麼我下邊簡單的列一個登錄頁。不會寫的童鞋能夠直接拿去用:spring
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <h1 align="center">歡迎光臨瓦爾哈拉-衆神管理系統</h1> <h2 align="center">凡人,若是想查看衆神系統 <a th:href="@{/login}">請登記</a></h2> <hr> <h3>巨人族</h3> <ul> <li><a th:href="@{/level1/1}">伊米爾</a></li> <li><a th:href="@{/level1/2}">蒂阿茲</a></li> <li><a th:href="@{/level1/3}">斯卡娣</a></li> </ul> <h3>其餘神祗</h3> <ul> <li><a th:href="@{/level2/1}">女神格歐費茵</a></li> <li><a th:href="@{/level2/2}">瓦利</a></li> <li><a th:href="@{/level2/3}">林德</a></li> </ul> <h3>主神</h3> <ul> <li><a th:href="@{/level3/1}">奧丁</a></li> <li><a th:href="@{/level3/2}">弗麗嘉</a></li> <li><a th:href="@{/level3/3}">索爾</a></li> </ul> </body> </html>
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <h1 align="center">歡迎登錄瓦爾哈拉-衆神管理系統</h1> <hr> <div align="center"> <form action="" method="post"> 用戶名:<input name=""/><br> 口令:<input name=""><br/> <input type="submit" value="登錄"> </form> </div> </body> </html>
level-1數據庫
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <a th:href="@{/}">返回</a> <h1>伊米爾</h1> <p>遠古洪荒時代,太虛混沌世界的中間有一條寬大無底的的金伽儂裂縫,冷、熱氣,火焰、冰塊、煙霧和蒸汽相互做用在裂縫邊緣造成了壅堵化成了巨人伊米爾。 伊米爾的後代奧丁、維利和威長大後殺死了伊米爾,用他的軀體造成了世界:血化成湖泊海洋;肉化爲土地;骨頭化爲山;牙齒化爲岩石; 腦髓化爲雲;頭蓋骨化爲天空;眉毛化爲一道柵欄。</p> </body> </html>
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <a th:href="@{/}">返回</a> <h1>蒂阿茲</h1> <p>巨人斯卡娣的父親。蒂阿茲綁架了掌管永葆青春的金蘋果女神伊敦恩。由於這是洛基私約伊敦恩外出闖的禍,衆神祗便勒令洛基前去搭救。 洛基借用弗麗嘉的羽衣變成一隻小鷹,潛入巨人蒂阿茲的城堡,將伊敦恩變成一枚核桃,銜在嘴裏飛向阿斯加爾德。 不料被斯卡娣發現,蒂阿茲聞訊變成一隻隼鷹追遇上來。在即將追上之際,提爾點燃火把,將隼鷹燒死,巨人蒂阿茲便損命。</p> </body> </html>
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <a th:href="@{/}">返回</a> <h1>斯卡娣</h1> <p>蒂阿茲之女、瓦尼爾部落主神尼奧爾德之妻。 父親蒂阿茲被衆神設計殺害後,斯卡娣向主神奧丁要求血案賠償,奧丁無奈允諾她能夠在衆神祗中物色一位丈夫, 斯卡娣看中了光明神巴德爾,奧丁天然不肯,遂略施手腕把斯卡娣嫁給了年老貌醜的海神尼奧爾德。 斯卡娣嫁給尼奧爾德不久隨即離婚。</p> </body> </html>
level2/3的本身根據level-1裏邊的類直接複製粘貼吧,基本都同樣的,把名字和內容改掉區分一下就好了,頁面的架構以下,放進resource-templates裏邊,這個都知道吧~安全
好,咱們經過啓動主程序,來訪問一下咱們的頁面~(別忘了controller,否則你啓動也訪問不到頁面),訪問:localhost:8080架構
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @Controller public class ValhallaController { private final String PREFIX = "pages/"; /** * 歡迎頁 * * @return */ @GetMapping("/") public String index() { return "welcome"; } /** * 登錄頁 * * @return */ @GetMapping("/userlogin") public String loginPage() { return PREFIX + "login"; } /** * level1頁面映射 * * @param path * @return */ @GetMapping("/level1/{path}") public String level1(@PathVariable("path") String path) { return PREFIX + "level1/" + path; } /** * level2頁面映射 * * @param path * @return */ @GetMapping("/level2/{path}") public String level2(@PathVariable("path") String path) { return PREFIX + "level2/" + path; } /** * level3頁面映射 * * @param path * @return */ @GetMapping("/level3/{path}") public String level3(@PathVariable("path") String path) { return PREFIX + "level3/" + path; } }
看來訪問的仍是比較成功的。app
1)、引入Spring Security模塊框架
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
2)、配置,這裏就須要使用一些註解了 @EnableWebSecurity
咱們先這樣,將咱們須要受權的規則頁和對應的用戶等級進行匹配。而後訪問一下頁面看看,好比咱們點擊上圖中巨人族的 伊米爾(你會發現,它提示咱們403,訪問拒絕)
@EnableWebSecurity public class MySercurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // super.configure(http); // 定製請求受權的規則 http.authorizeRequests().antMatchers("/").permitAll() .antMatchers("/level1/**").hasRole("VIP1") .antMatchers("/level2/**").hasRole("VIP2") .antMatchers("/level3/**").hasRole("VIP3"); } }
這是由於咱們尚未登陸訪問,做爲遊客,確定是不匹配咱們上邊寫的規則的,因此接下來,先解決非等級的一個跳轉
加入以下的這段代碼,讓它爲咱們自動配置,只要不符合等級規則,就跳轉到/login的登陸頁(一樣點擊伊米爾再看)
// 開啓自動配置的登陸功能, http.formLogin();
3)、認證的規則設定
講咱們擁有的用戶信息對應匹配規則,而後使用這些帳戶登陸試一下~(這裏爲了簡單說明,我用的是內存保存,固然,可使用「auth.jdbc.....」來進行數據庫的連接使用)
// 自定義認證規則 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // super.configure(auth); auth.inMemoryAuthentication().withUser("human").password("123456").roles("VIP1") .and() .withUser("angle").password("123456").roles("VIP1", "VIP2") .and() .withUser("god").password("123456").roles("VIP1", "VIP2", "VIP3"); }
當咱們再次訪問,而且用上邊匹配的用戶登陸的時候卻發現這樣一個問題,說咱們的密碼id是空(There is no PasswordEncoder mapped for the id "null"),這是因爲spring security5.0+的版本,多一個密碼存儲格式官方指定的格式是:「{id}.....」,這個id就是加密方式,id能夠是bcrypt、sha256等,後面跟着的是加密後的密碼。官方推薦的事bcrypt的加密方式。以下:(若是你使用的jdbc的方式,那麼在存儲的時候就已經以這種方式存儲了,只須要在密碼上寫入就行了)
// 自定義認證規則 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // super.configure(auth); auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()) .withUser("human").password(new BCryptPasswordEncoder().encode("123456")).roles("VIP1") .and() .withUser("angle").password(new BCryptPasswordEncoder().encode("123456")).roles("VIP1", "VIP2") .and() .withUser("god").password(new BCryptPasswordEncoder().encode("123456")).roles("VIP1", "VIP2", "VIP3"); }
咱們再次登陸就正常了。
3)、註銷功能
咱們有了用戶的登陸,固然是須要一個註銷,也就是退出登陸的功能,這個功能就比較簡單了,只須要在上邊的代碼中加入一段以下的戲份就能夠實現了。
1.在welcome.html中加入註銷按鈕的相關信息;
2.在MySecurity的configure中加入註銷方法;
<!-- 把這段代碼放到你想要的位置,好比登陸前邊 -->
<form th:action="@{/logout}" method="post"> <input type="submit" value="註銷"> </form>
.....
// 開啓自動配置的登陸功能,
http.formLogin(); // 開啓自動配置的註銷功能,若是註銷成功返回「/」頁 http.logout().logoutSuccessUrl("/");
4)、優化頁面的展現
讓不一樣用戶看到首頁展現不一樣,沒有登陸的呢,就看不到具體內容。
1.在pom裏邊引入springsecurity4;
2.在html中改造一下,引入springsecurity4(加兩個div作判斷是否受權-sec:authorize),若是沒有認證(!isAuthenticated())就展現遊客請登陸,若是認證了就展現角色信息;讀取角色信息(sec:authentication);
<dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity4</artifactId> </dependency>
<body> <h1 align="center">歡迎光臨瓦爾哈拉-衆神管理系統</h1> <div sec:authorize="!isAuthenticated()"> <h2 align="center">凡人,若是想查看衆神系統 <a th:href="@{/login}">請登陸</a></h2> </div> <div sec:authorize="isAuthenticated()"> <h2><span sec:authentication="name"></span>,您好,您的角色有: <span sec:authentication="principal.authorities"></span></h2> <form th:action="@{/logout}" method="post"> <input type="submit" value="註銷"/> </form> </div> <hr> <div sec:authorize="hasRole('VIP1')"> <h3>巨人族</h3> <ul> <li><a th:href="@{/level1/1}">依米爾</a></li> <li><a th:href="@{/level1/2}">蒂阿茲</a></li> <li><a th:href="@{/level1/3}">斯卡蒂</a></li> </ul> </div> <div sec:authorize="hasRole('VIP2')"> <h3>其餘...</h3> ... </div> <div sec:authorize="hasRole('VIP3')"> <h3>...</h3>
...
</div>
再次訪問頁面,就會看到差異了:(截圖是登陸成功的)
5)、完善功能。(綠色加粗部分就是新添的代碼)
1.【MySecurityConfig】登陸通常都會有「記住我」這個功能,因此要加入remember me;
2.【config+login】登陸請求變爲login,並進入到咱們本身的登陸頁,並將用戶名和密碼作關聯;
3.【welcome.html】優化,將登陸請求變成userlogin請求;
.antMatchers("/level3/**").hasRole("VIP3");
http.formLogin().usernameParameter("user").passwordParameter("pwd").loginPage("/userlogin");
http.rememberMe().rememberMeParameter("remember"); http.logout().logoutSuccessUrl("/");
<div align="center"> <form th:action="@{/userlogin}" method="post"> 用戶名:<input name="user"/><br> 密碼:<input name="pwd"><br/> <input type="checkbox" name="remember">記住我<br/> <input type="submit" value="登錄"> </form> </div>
<div sec:authorize="!isAuthenticated()"> <h2 align="center">凡人,若是想查看衆神系統 <a th:href="@{/userlogin}">請登陸</a></h2> </div>
以上,咱們來訪問一下看看本身的登陸系統吧~