- Spring Boot 教程
- Spring Boot - 首頁
- Spring Boot - 簡介
- Spring Boot - 快速入門
- Spring Boot - 啟動
- Spring Tool Suite
- Spring Boot - Tomcat 部署
- Spring Boot - 構建系統
- Spring Boot - 程式碼結構
- Spring Bean & 依賴注入
- Spring Boot - 執行器
- Spring Boot - 啟動器
- Spring Boot - 應用屬性
- Spring Boot - 配置
- Spring Boot - 註解
- Spring Boot - 日誌
- 構建RESTful Web 服務
- Spring Boot - 異常處理
- Spring Boot - 攔截器
- Spring Boot - Servlet 過濾器
- Spring Boot - Tomcat 埠號
- Spring Boot - Rest 模板
- Spring Boot - 檔案處理
- Spring Boot - 服務元件
- Spring Boot - Thymeleaf
- 使用RESTful Web 服務
- Spring Boot - CORS 支援
- Spring Boot - 國際化
- Spring Boot - 定時任務
- Spring Boot - 啟用HTTPS
- Spring Boot - Eureka 伺服器
- 使用Eureka註冊服務
- 閘道器代理伺服器和路由
- Spring Cloud 配置伺服器
- Spring Cloud 配置客戶端
- Spring Boot - Actuator
- Spring Boot - Admin 伺服器
- Spring Boot - Admin 客戶端
- Spring Boot - 啟用Swagger2
- Spring Boot - 使用SpringDoc OpenAPI
- Spring Boot - 建立Docker映象
- 追蹤微服務日誌
- Spring Boot - Flyway 資料庫
- Spring Boot - 傳送郵件
- Spring Boot - Hystrix
- Spring Boot - WebSocket
- Spring Boot - 批處理服務
- Spring Boot - Apache Kafka
- Spring Boot - Twilio
- Spring Boot - 單元測試用例
- Rest Controller 單元測試
- Spring Boot - 資料庫處理
- 保護Web應用程式
- Spring Boot - 基於JWT的OAuth2
- Spring Boot - Google Cloud Platform
- Spring Boot - Google OAuth2 登入
- Spring Boot 資源
- Spring Boot - 快速指南
- Spring Boot - 有用資源
- Spring Boot - 討論
Spring Boot - 基於JWT的OAuth2
本章將詳細講解Spring Boot安全機制以及基於JWT的OAuth2。
注意 - 此示例使用Spring Boot 1.5.9編寫。在Spring Boot 3中,Spring Security授權伺服器已棄用。
授權伺服器
授權伺服器是Web API安全性的一個高階架構元件。授權伺服器充當集中的授權點,允許您的應用程式和HTTP端點識別應用程式的功能。
資源伺服器
資源伺服器是一個向客戶端提供訪問令牌以訪問資源伺服器HTTP端點的應用程式。它是一組包含HTTP端點、靜態資源和動態網頁的庫。
OAuth2
OAuth2是一個授權框架,使應用程式Web安全能夠訪問客戶端的資源。要構建OAuth2應用程式,我們需要關注授權型別(授權碼)、客戶端ID和客戶端金鑰。
JWT令牌
JWT令牌是JSON Web令牌,用於表示雙方之間安全的宣告。您可以在www.jwt.io/瞭解更多關於JWT令牌的資訊。
現在,我們將構建一個OAuth2應用程式,該應用程式藉助JWT令牌啟用授權伺服器和資源伺服器的使用。
您可以使用以下步驟透過訪問資料庫來實現帶有JWT令牌的Spring Boot安全。
首先,我們需要在構建配置檔案中新增以下依賴項。
Maven使用者可以在您的pom.xml檔案中新增以下依賴項。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <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> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency>
Gradle使用者可以在build.gradle檔案中新增以下依賴項。
compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.boot:spring-boot-starter-web')
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('org.springframework.security:spring-security-test')
compile("org.springframework.security.oauth:spring-security-oauth2")
compile('org.springframework.security:spring-security-jwt')
compile("org.springframework.boot:spring-boot-starter-jdbc")
compile("com.h2database:h2:1.4.191")
其中,
Spring Boot Starter Security - 實現Spring Security
Spring Security OAuth2 - 實現OAUTH2結構以啟用授權伺服器和資源伺服器。
Spring Security JWT - 為Web安全生成JWT令牌
Spring Boot Starter JDBC - 訪問資料庫以確保使用者是否存在。
Spring Boot Starter Web - 編寫HTTP端點。
H2資料庫 - 儲存用於身份驗證和授權的使用者的資訊。
完整的構建配置檔案如下所示。
pom.xml
<?xml version = "1.0" encoding = "UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.tutorialspoint</groupId>
<artifactId>websecurityapp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>websecurityapp</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<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>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Gradle – build.gradle
buildscript {
ext {
springBootVersion = '1.5.9.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
group = 'com.tutorialspoint'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.boot:spring-boot-starter-web')
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('org.springframework.security:spring-security-test')
compile("org.springframework.security.oauth:spring-security-oauth2")
compile('org.springframework.security:spring-security-jwt')
compile("org.springframework.boot:spring-boot-starter-jdbc")
compile("com.h2database:h2:1.4.191")
}
現在,在主要的Spring Boot應用程式中,新增`@EnableAuthorizationServer`和`@EnableResourceServer`註解,以便在同一個應用程式中充當身份驗證伺服器和資源伺服器。
此外,您可以使用以下程式碼編寫一個簡單的HTTP端點,以使用JWT令牌透過Spring Security訪問API。
WebsecurityappApplication.java
package com.tutorialspoint.websecurityapp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@EnableAuthorizationServer
@EnableResourceServer
@RestController
public class WebsecurityappApplication {
public static void main(String[] args) {
SpringApplication.run(WebsecurityappApplication.class, args);
}
@RequestMapping(value = "/products")
public String getProductName() {
return "Honey";
}
}
使用以下程式碼定義POJO類以儲存用於身份驗證的使用者的資訊。
UserEntity.java
package com.tutorialspoint.websecurityapp;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
public class UserEntity {
private String username;
private String password;
private Collection<GrantedAuthority> grantedAuthoritiesList = new ArrayList<>();
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Collection<GrantedAuthority> getGrantedAuthoritiesList() {
return grantedAuthoritiesList;
}
public void setGrantedAuthoritiesList(Collection<GrantedAuthority> grantedAuthoritiesList) {
this.grantedAuthoritiesList = grantedAuthoritiesList;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
現在,使用以下程式碼並定義CustomUser類,該類擴充套件org.springframework.security.core.userdetails.User類以進行Spring Boot身份驗證。
CustomUser.java
package com.tutorialspoint.websecurityapp;
import org.springframework.security.core.userdetails.User;
public class CustomUser extends User {
private static final long serialVersionUID = 1L;
public CustomUser(UserEntity user) {
super(user.getUsername(), user.getPassword(), user.getGrantedAuthoritiesList());
}
}
您可以建立`@Repository`類來從資料庫讀取使用者資訊並將其傳送到Custom使用者服務,還可以新增授予的許可權“ROLE_SYSTEMADMIN”。
OAuthDao.java
package com.tutorialspoint.websecurityapp;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Repository;
@Repository
public class OAuthDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public UserEntity getUserDetails(String username) {
Collection<GrantedAuthority> grantedAuthoritiesList = new ArrayList<>();
String userSQLQuery = "SELECT * FROM USERS WHERE USERNAME=?";
List<UserEntity> list = jdbcTemplate.query(userSQLQuery, new String[] { username },
(ResultSet rs, int rowNum) -> {
UserEntity user = new UserEntity();
user.setUsername(username);
user.setPassword(rs.getString("PASSWORD"));
return user;
});
if (list.size() > 0) {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_SYSTEMADMIN");
grantedAuthoritiesList.add(grantedAuthority);
list.get(0).setGrantedAuthoritiesList(grantedAuthoritiesList);
return list.get(0);
}
return null;
}
}
您可以建立一個Custom User detail service類,該類擴充套件org.springframework.security.core.userdetails.UserDetailsService以呼叫DAO儲存庫類,如下所示。
CustomDetailsService.java
package com.tutorialspoint.websecurityapp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class CustomDetailsService implements UserDetailsService {
@Autowired
OAuthDao oauthDao;
@Override
public CustomUser loadUserByUsername(final String username) throws UsernameNotFoundException {
UserEntity userEntity = null;
try {
userEntity = oauthDao.getUserDetails(username);
CustomUser customUser = new CustomUser(userEntity);
return customUser;
} catch (Exception e) {
e.printStackTrace();
throw new UsernameNotFoundException("User " + username + " was not found in the database");
}
}
}
接下來,建立一個`@configuration`類來啟用Web安全,定義密碼編碼器(BCryptPasswordEncoder),並定義AuthenticationManager bean。安全配置類應擴充套件WebSecurityConfigurerAdapter類。
SecurityConfiguration.java
package com.tutorialspoint.websecurityapp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private CustomDetailsService customDetailsService;
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
@Override
@Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customDetailsService).passwordEncoder(encoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
現在,定義OAuth2配置類以新增客戶端ID、客戶端金鑰、定義JwtAccessTokenConverter、私鑰和公鑰作為令牌簽名金鑰和驗證金鑰,併為具有作用域的令牌有效性配置ClientDetailsServiceConfigurer。
OAuth2Config.java
package com.tutorialspoint.websecurityapp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
@Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
private String clientid = "tutorialspoint";
private String clientSecret = "my-secret-key";
private String privateKey = "private key";
private String publicKey = "public key";
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Bean
public JwtAccessTokenConverter tokenEnhancer() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(privateKey);
converter.setVerifierKey(publicKey);
return converter;
}
@Bean
public JwtTokenStore tokenStore() {
return new JwtTokenStore(tokenEnhancer());
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore())
.accessTokenConverter(tokenEnhancer());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient(clientid).secret(clientSecret).scopes("read", "write")
.authorizedGrantTypes("password", "refresh_token").accessTokenValiditySeconds(20000)
.refreshTokenValiditySeconds(20000);
}
}
金鑰建立
現在,使用openssl建立私鑰和公鑰。
您可以使用以下命令生成私鑰。
openssl genrsa -out jwt.pem 2048 openssl rsa -in jwt.pem
您可以使用以下命令生成公鑰。
openssl rsa -in jwt.pem -pubout
對於高於1.5版本的Spring Boot,請在您的application.properties檔案中新增以下屬性以定義OAuth2資源過濾器順序。
security.oauth2.resource.filter-order=3
YAML檔案使用者可以在YAML檔案中新增以下屬性。
security:
oauth2:
resource:
filter-order: 3
現在,在類路徑資源**src/main/resources/directory**下建立schema.sql和data.sql檔案以將應用程式連線到H2資料庫。
schema.sql檔案如下所示:
schema.sql
CREATE TABLE USERS (ID INT PRIMARY KEY, USERNAME VARCHAR(45), PASSWORD VARCHAR(60));
data.sql檔案如下所示:
data.sql
INSERT INTO USERS (ID, USERNAME,PASSWORD) VALUES ( 1, 'tutorialspoint@gmail.com','$2a$08$fL7u5xcvsZl78su29x1ti.dxI.9rYO8t0q5wk2ROJ.1cdR53bmaVG'); INSERT INTO USERS (ID, USERNAME,PASSWORD) VALUES ( 2, 'myemail@gmail.com','$2a$08$fL7u5xcvsZl78su29x1ti.dxI.9rYO8t0q5wk2ROJ.1cdR53bmaVG');
注意 - 密碼應以Bcrypt編碼器的格式儲存在資料庫表中。
編譯和執行
您可以建立一個可執行的JAR檔案,並使用以下Maven或Gradle命令執行Spring Boot應用程式。
對於Maven,您可以使用以下命令:
mvn clean install
“BUILD SUCCESS”之後,您可以在target目錄下找到JAR檔案。
對於Gradle,您可以使用如下所示的命令:
gradle clean build
“BUILD SUCCESSFUL”之後,您可以在build/libs目錄下找到JAR檔案。
現在,使用此處顯示的命令執行JAR檔案:
java –jar <JARFILE>
應用程式在Tomcat埠8080上啟動。
現在透過POSTMAN點選POST方法URL以獲取OAUTH2令牌。
https://:8080/oauth/token
現在,新增如下所示的請求頭:
Authorization - 使用您的客戶端ID和客戶端金鑰進行基本身份驗證。
Content Type - application/x-www-form-urlencoded
現在,新增如下所示的請求引數:
- grant_type = password
- username = 你的使用者名稱
- password = 你的密碼
現在,點選API並獲取access_token,如下所示:
現在,在請求頭中使用Bearer access token點選資源伺服器API,如下所示。
然後,您可以看到如下所示的輸出: