1. 程式人生 > 實用技巧 >Understand Spring Security Architecture and implement Spring Boot Security

Understand Spring Security Architecture and implement Spring Boot Security

In this tutorial we will be looking at how Spring Security works and its architecture. We will be creating a Spring Boot Project to expose two REST API's

研究Spring Security的工作方式及其架構

  • /helloadmin
  • /hellouser

We will then be implementing Spring Security such that a client having Admin role will be able to access both /helloadmin and /hellouser API

. While a client having User role will be able to access only /hellouser API

Spring Boot Project to expose REST API's

Our Maven Project at the end of this tutorial will be as follows-

Go to Spring Initializr website and create a new Spring Boot Project. We will only include the Web dependency now.

The pom.xml will be as follows-

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.1.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.javainuse</groupId>
	<artifactId>spring-security</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-security</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

Next create the Controller class to expose the REST API's -

package com.javainuse.springsecurity.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ResourceController {
	
	@RequestMapping({"/hellouser"})
	public String helloUser(){
		return "Hello User";
	}
	
	@RequestMapping({"/helloadmin"})
	public String helloAdmin(){
		return "Hello Admin";
	}
}

If we now start our Spring Boot project, we will be able to hit the webservice using Postman -
Test /hellouser API

Test /helloadmin API

Configure Spring Security for Spring Boot Project

We will be configuring Spring Security for the Spring Boot project we just created. Let us first understand the Spring Security Architecture.

將為剛剛建立的Spring Boot專案配置Spring Security。讓我們首先了解一下Spring Security Architecture

Understanding Spring Security Architecture

Let us understand how Spring Security Works.

Spring Boot Security Architecture

Filters - Before the request reaches the Dispatcher Servlet, it is first intercepted by a chain of filters.

過濾器-在請求到達分派器Servlet之前,它首先被一系列過濾器攔截

These filters are responsible for Spring Security. So any incoming request will go through these filters and it is here that authentication and authorization takes place. Based on the type of requests there are different Authentication Filters like the BasicAuthenticationFilter,UsernamePasswordAuthenticationFilter etc

這些過濾器負責Spring Security。因此,任何傳入的請求都將通過這些過濾器,並且在此處進行身份驗證和授權。根據請求的型別,有不同的身份驗證過濾器,例如BasicAuthenticationFilter,UsernamePasswordAuthenticationFilter等

Authentication Object Creation - When the request is intercepted by the appropriate AuthenticationFilter it retrieves the username and password from the request and creates the Authentication Object. If the extracted credentials are username and password, then UsernamePasswordAuthenticationToken is created.

當請求被適當的AuthenticationFilter攔截後,它將從請求中檢索使用者名稱和密碼,並建立Authentication Object。如果提取的憑據是使用者名稱和密碼,則將建立UsernamePasswordAuthenticationToken。

AuthenicationManager - Using the Authentication Object created the filter will then call the authenticate method of the Authentication Manager. The Authentication Manager is only a interface and actual implementation of the authenticate method is provided by the ProviderManager.

AuthenicationManager-使用建立的身份驗證物件,過濾器將呼叫身份驗證管理器的authenticate方法。Authentication Manager只是一個介面,ProviderManager提供了authenticate方法的實際實現

Important point to note here is that the Authentication Manager takes an Authentication object as input and after successful authentication again returns an object of type Authentication.

這裡要注意的重要一點是,身份驗證管理器將身份驗證物件作為輸入,並且在成功身份驗證之後再次返回身份驗證型別的物件

The ProviderManager has a list of AuthenticationProviders. From it's authenticate method it calls the authenticate method of the appropriate AuthenticateProvider. In response it gets the Principal Authentication Object if the authentication is successful.

ProviderManager具有AuthenticationProviders列表。從它的authenticate方法中,它呼叫適當的AuthenticateProvider的authenticate方法。作為響應,如果身份驗證成功,它將獲取主體身份驗證物件

AuthenticationProvider - The AuthenicationProvider is an interface with a single authenticate method.

AuthenticationProvider-AuthenicationProvider是具有單一身份驗證方法的介面

It has various implementations like CasAuthenticationProvider,DaoAuthenticationProvider. Depending on the implementation an appropriate AuthenicationProvider implementation is used. It is in the AuthenticationProvider Implementation authenticate method where all the actual authentication takes place.

它具有各種實現,例如CasAuthenticationProvider,DaoAuthenticationProvider。根據實現,使用適當的AuthenicationProvider實現。它是在AuthenticationProvider實現身份驗證方法中進行的所有實際身份驗證

Using the UserDetails service the AuthenticationProvider fetches the User Object corresponding to the username. It fetches this User Object from either a database, internal memory or other sources. This User object credentials are then compared with the incoming Authentication Object credentials. If Authentication is successful then the Principal Authentication Object is returned in response.

使用UserDetails服務,AuthenticationProvider提取與使用者名稱相對應的使用者物件。它從資料庫,內部儲存器或其他來源獲取此使用者物件。然後將該使用者物件憑據與傳入的身份驗證物件憑據進行比較。如果身份驗證成功,則將返回主體身份驗證物件作為響應

UserDetailsService - The UserDetailsService is an interface having a single method named loadUserByUsername.

UserDetailsService是具有名為loadUserByUsername的單個方法的介面

It has various implementations CachingUserDetailsService, JDBCDaoImpl etc. Based on the implementation an appropriate UserDetailsService is called.

它具有CachingUserDetailsService,JDBCDaoImpl等各種實現。基於該實現,將呼叫適當的UserDetailsService

It is responsible for fetching the User Object with username and password against which the incoming User Object will be compared.

它負責獲取帶有使用者名稱和密碼的使用者物件,並將輸入的使用者物件與之進行比較

Add Spring Security to Spring Boot

We will need to add the Spring Security Starter dependency in the 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.0.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.javainuse</groupId>
	<artifactId>spring-security</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-security</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</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-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

As soon as we add the spring security dependency to the project the basic authentication gets activated by default. If we now start the application, Basic Security is enabled by default by Spring security due to the spring auto configurations.

一旦將spring安全依賴項新增到專案中,預設情況下就會啟用基本身份驗證。如果現在啟動應用程式,則由於spring自動配置,Spring安全預設情況下會啟用Basic Security

In the console we get the password while the username is user-

Let us have a look Spring Security Autoconfigurations.

  • When no Spring Security dependency is added -

  • When Spring Security is added -

We will now be creating our own custom Spring Security Configuration by extending the WebSecurityConfigurerAdapter In this class we will be making use of the PasswordEncoder. In previous tutorial we have already seen the need for password encoder.

現在,我們將通過擴充套件WebSecurityConfigurerAdapter來建立自己的自定義Spring Security配置。在此類中,我們將使用PasswordEncoder。在先前的教程中,我們已經看到了對密碼編碼器的需求

package com.javainuse.springsecurity.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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
public class SpringSecurityConfiguration  extends WebSecurityConfigurerAdapter{
	
	@Autowired
	CustomUserDetailsService userDetailsService;
	
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
	@Override
	public void configure(AuthenticationManagerBuilder auth) throws Exception
	{
		auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
	}
	
	@Override
	protected void configure(HttpSecurity httpSecurity) throws Exception {
		// We don't need CSRF for this example
		httpSecurity.csrf().disable()
				.authorizeRequests().antMatchers("/helloadmin")
				.hasRole("ADMIN")
				.antMatchers("/hellouser")
				.hasAnyRole("ADMIN","USER")
				.and().httpBasic();
	}

}

Create a custom UserDetails Service class-

package com.javainuse.springsecurity.config;

import java.util.Arrays;
import java.util.List;

import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class CustomUserDetailsService implements UserDetailsService {

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		List<SimpleGrantedAuthority> roles=null;
		if(username.equals("admin"))
		{
		roles = Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN"));
		return new User("admin", "$2y$12$I0Di/vfUL6nqwVbrvItFVOXA1L9OW9kLwe.1qDPhFzIJBpWl76PAe",
					roles);
		}
		else if(username.equals("user"))
		{
		roles = Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
		return new User("user", "$2y$12$VfZTUu/Yl5v7dAmfuxWU8uRfBKExHBWT1Iqi.s33727NoxHrbZ/h2",
					roles);
		}
		throw new UsernameNotFoundException("User not found with username: " + username);
	}

}

Start the Spring Boot Application

Download Source Code

Download it -
Spring Boot + Security