簡介
作為 Spring 全家桶組件之一,Spring Security 是一個提供安全機制的組件,它主要解決兩個問題:
- 認證:驗證用戶名和密碼;
- 授權:對於不同的 URL 權限不一樣,只有當認證的用戶擁有某個 URL 的需要的權限時才能訪問。
Spring Security 底層使用的是過濾器,針對 URL 進行的攔截,對應到 Java 中也就是類; 因此被稱為粗粒度授權驗證,就是驗證 URL ,當前用戶是否有這個 URL 的權限。
入門
創建項目
使用 Idea 創建 Spring Boot 項目,勾選需要的組件:
- Spring Web
- Spring Security
或者創建項目后添加依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
這里使用的是 JSP 作為模板,有關如何在 Spring Boot 中使用 JSP 作為模板請訪問:https://www.cnblogs.com/cloudfloating/p/11787222.html
WebSecurityConfig
package top.cloudli.demo.security;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
PasswordEncoder encoder = new BCryptPasswordEncoder();
auth.inMemoryAuthentication()
.passwordEncoder(encoder)
.withUser("root")
.password(encoder.encode("root@123456"))
.roles("ROOT", "USER")
.and()
.withUser("user")
.password(encoder.encode("user@123456"))
.roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/css/**")
.permitAll() // css 不用驗證
.anyRequest()
.authenticated() // 其它頁面全部需要驗證
.and()
.formLogin() // 使用默認登錄頁面
.and()
.exceptionHandling()
.accessDeniedPage("/401") // 無權限時跳轉的頁面
.and()
.logout();
}
}
@EnableWebSecurity
注解啟用驗證;@EnableGlobalMethodSecurity(prePostEnabled=true)
注解允許我們在控制器的方法中使用@PreAuthorize
實現權限分割。
此處創建了兩個用戶並保存在內存中,分別是擁有 ROOT 和 USER 權限的 root 用戶和僅擁有 USER 權限的 user 用戶。
fromLogin()
方法可以接着調用 loginPage()
指定一個自定義登錄頁面,這里使用的是默認登錄頁面。
編寫頁面
1.index.jsp,所有通過驗證的用戶都可以訪問:
<%--
任何通過驗證的用戶都能訪問的頁面
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Spring Security Demo Application</title>
<link rel="stylesheet" type="text/css" href="css/style.css">
</head>
<body>
<div class="content">
<h1>Spring Security In Memory Authentication</h1>
<h2>這是被保護的頁面(ROLE_USER)。</h2>
</div>
</body>
</html>
2.root.jsp,只有擁有 ROOT 權限的用戶能訪問:
<%--
需要 ROLE_ROOT 才能訪問的頁面
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Root Page</title>
<link rel="stylesheet" type="text/css" href="css/style.css">
</head>
<body>
<div class="content">
<h1>Root Page</h1>
<h2>你正在訪問受保護的頁面(ROLE_ROOT)。</h2>
</div>
</body>
</html>
3.401.jsp,沒有權限時跳轉的頁面:
<%--
權限不夠時跳轉的頁面
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>401 Unauthorized</title>
<link rel="stylesheet" type="text/css" href="css/style.css">
</head>
<body class="error">
<div class="content">
<h1>401 Unauthorized!</h1>
<h2>你沒有權限訪問此頁面。</h2>
</div>
</body>
</html>
控制器
package top.cloudli.demo.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class DemoController {
@PreAuthorize("hasAnyAuthority('ROLE_USER')")
@GetMapping("/")
public String index() {
return "index";
}
@PreAuthorize("hasAnyAuthority('ROLE_ROOT')")
@GetMapping("/root")
public String root() {
return "root";
}
@GetMapping("/401")
public String accessDenied() {
return "401";
}
}
@PreAuthorize
注解指定了訪問頁面所需要的權限,這里的權限要加上 ROLE_
前綴。
Run
訪問 http://localhost:8080/
將進入登錄頁面(這里使用的是 Spring Security 的默認登錄頁面):
使用剛才創建的內存用戶 user 登錄后將返回 index 頁面:
訪問 http://localhost:8080/root
,由於 user 用戶沒有 ROLE_ROOT 權限,跳轉到 401 頁面:
訪問 http://localhost:8080/logout
將進入默認登出頁面:
這里的登錄和登出頁面均可以使用自定義頁面,只需要在自定義的頁面中把數據通過 PSOT 請求提交到 /login
或 /logout
即可完成登錄和登出。