preface
In Web API interface services scenarios, user authentication and authentication is a common requirement. Spring Security is said to be the de facto standard in this field. In practice, the overall design does have many commendable points. It also proves to some extent that what the guys often say is “too complicated” is also true.
This article takes a simple SpringBoot Web application as an example and focuses on the following:
-
Demonstrate the configuration method of Spring Security interface authentication and authentication.
-
This section uses memory and database as examples to describe the storage and reading mechanisms of authentication and authentication data.
-
Custom implementation of several modules, including: authentication filter, authentication or authentication failure handler, etc.
SpringBoot sample
Create a SpringBoot example to demonstrate the application of Spring Security in the SpringBoot environment. There are four parts: Pom.xml, Application. yml, IndexController, and HelloController.
SpringBoot pom.xml
. <artifactId>boot-example</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </dependency> </dependencies>Copy the code
Boot-example is the SpringBoot project submodule (Module) for demonstration purposes.
Note: The version of the dependency is declared in the project POM.xml dependencyManagement.
SpringBoot application.yml
spring:
application:
name: example
server:
port: 9999
logging:
level:
root: info
Copy the code
The SpringBoot application name is Example, and the instance port is 9999.
SpringBoot IndexController
@RestController @RequestMapping("/") public class IndexController { @GetMapping public String index() { return "index"; }}Copy the code
IndexController implements an interface: /.
SpringBoot HelloController
@RestController @RequestMapping("/hello") public class HelloController { @GetMapping("/world") public String world() { return "hello world"; } @GetMapping("/name") public String name() { return "hello name"; }}Copy the code
HelloController implements two interfaces: /hello/world and /hello/name.
Compile and start the SpringBoot application, request the interface through the browser, request the path, and response result:
http://localhost:9999
index
http://localhost:9999/hello/world
hello world
http://localhost:9999/hello/name
hello name
Copy the code
The SpringBoot example is ready.
SpringBoot integrates Spring Security
To integrate Spring Security with SpringBoot, you only need to add the dependency spring-boot-starter-security to pop. XML, as follows:
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
Copy the code
To compile the startup application, we can see two special lines of log on the command line terminal, as opposed to the normal SpringBoot application:
The 2022-01-09 16:05:57. 87581-437 the INFO [main]. S.S.U serDetailsServiceAutoConfiguration: Using generated security password: 3 ef27867 - e938 f0deab7b 16:05:57 2022-01-09-4 fa4 - b5da - 5015. 87581-525 the INFO [main] O.S.S.W eb. DefaultSecurityFilterChain : Will secure any request with [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@11e355ca, org.springframework.security.web.context.SecurityContextPersistenceFilter@5114b7c7, org.springframework.security.web.header.HeaderWriterFilter@24534cb0, org.springframework.security.web.csrf.CsrfFilter@77c233af, org.springframework.security.web.authentication.logout.LogoutFilter@5853ca50, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@6d074b14, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@3206174f, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@70d63e05, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@5115f590, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@767f6ee7, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@7b6c6e70, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@e11ecfa, org.springframework.security.web.session.SessionManagementFilter@106d77da, org.springframework.security.web.access.ExceptionTranslationFilter@7b66322e, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@3e5fd2b1]Copy the code
Indicates that Spring Security has taken effect in the SpringBoot application. By default, Spring Security automatically helps us do three things:
- Enable the FormLogin login authentication mode.
We use the browser request interface / :
http://localhost:9999/
Copy the code
You’ll see that the request is redirected to the page /login:
http://localhost:9999/login
Copy the code
Prompt to login with username and password:
- Generate a user name and password for login;
The user name is user, and the password is output to the startup log of the application:
Using generated security password: 3ef27867-e938-4fa4-b5da-5015f0deab7b
Copy the code
Every time an application is started, the password is regenerated randomly.
- Register filters for authentication and authentication;
Spring Security is essentially implemented through filters, or chains of filters, through which each interface request is sequentially “filtered” and each filter takes on its own responsibilities, which are combined to complete authentication and authentication.
The registered filters will vary depending on the configuration. By default, the list of loaded filters can refer to the startup log:
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
CsrfFilter
LogoutFilter
UsernamePasswordAuthenticationFilter
DefaultLoginPageGeneratingFilter
DefaultLogoutPageGeneratingFilter
BasicAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
Copy the code
Sign in using the user name and password Spring Security generated for us by default. Upon success, we are automatically redirected to / :
index
Copy the code
We can then request /hello/world and /hello/name via the browser as normal.
By default, Spring Security only supports formLogin-based authentication. Only fixed user names and randomly generated passwords can be used. Authentication is not supported. If you want to use richer security features:
-
Other authentication modes, such as HttpBasic
-
Customize the user name and password
-
authentication
We need to customize Spring Security configuration. Custom configuration can be implemented in two ways:
-
Java Configuration: Configuration using Java code
-
Security NameSpace Configuration: Configures the NameSpace in AN XML file
Based on Java as an example to introduce the Configuration, we need to provide a class inherits from WebSecurityConfigurerAdapter Configuration, and then by rewriting some methods for implementing custom configurations.
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
}
}
Copy the code
SecurityConfig using the @ Configuration annotations (Configuration), inherited from WebSecurityConfigurerAdapter, custom Configuration was achieved by rewriting the configure method in this paper.
Note: WebSecurityConfigurerAdapter have multiple names for the configure overloading method, used here is of type HttpSecurity method.
Note: For the default automatic Configuration of Spring Security, see Spring Boot Auto Configuration.
Spring Security uses HttpBasic authentication
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize ->
authorize
.anyRequest()
.authenticated())
.httpBasic();
}
Copy the code
http.authorizeHttpRequests()
Used to specify which requests require what authentication or authorization, anyRequest() and authenticated() mean that all requests require authentication.
http.authorizeHttpRequests()
Indicates that we use HttpBasic authentication.
After compiling and starting the application, the terminal will still output the password:
Using generated security password: e2c77467-8c46-4fe1-ab32-eb87558b8c0e
Copy the code
Because, we are only changing the authentication method.
CURL CURL CURL CURL CURL CURL CURL CURL CURL
Curl http://localhost:9999 {"timestamp": "2022-01-10t2:47:20.820 +00:00", "status": 401, "error": "Unauthorized", "path": "/" }Copy the code
We are prompted to Unauthorized, that is, there is no authentication.
Add the request header parameter Authorization as required by HttpBasic.
Basic Base64(user:e2c77467-8c46-4fe1-ab32-eb87558b8c0e)
Copy the code
That is:
Basic dXNlcjplMmM3NzQ2Ny04YzQ2LTRmZTEtYWIzMi1lYjg3NTU4YjhjMGU=
Copy the code
Request the interface again:
curl -H "Authorization: Basic dXNlcjplMmM3NzQ2Ny04YzQ2LTRmZTEtYWIzMi1lYjg3NTU4YjhjMGU=" http://localhost:9999
index
Copy the code
Authentication succeeds, and the interface responds normally.
Spring Security customizes user names and passwords
The default username and random password are not flexible enough. Most scenarios require us to support multiple users and set corresponding passwords for them respectively, which involves two problems:
-
How to read user name and password (query)
-
How to store username and password (add/Delete/change)
For reading, Spring Security designs the UserDetailsService interface:
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
Copy the code
loadUserByUsername
The implementation loads the corresponding UserDetails from a storage medium according to the username (username).
username
User name. The user name written by the client when sending a request.
UserDetails
User information, including the user name, password, and permission.
Note: The user information is more than the username and password.
For storage, Spring Security designs the UserDetailsManager interface:
public interface UserDetailsManager extends UserDetailsService {
void createUser(UserDetails user);
void updateUser(UserDetails user);
void deleteUser(String username);
void changePassword(String oldPassword, String newPassword);
boolean userExists(String username);
}
Copy the code
createUser
Creating User Information
updateUser
Modifying User Information
deleteUser
Deleting User Information
changePassword
Example Change the password of the current user
userExists
Check whether the user exists
Note: UserDetailsManager inherits from UserDetailsService.
In other words, we can provide a class that has implemented the interface UserDetailsManager*, rewrite some of its methods, and define the storage and reading logic of user name, password and other information based on some storage media. You can then inject an instance of this class into Spring Security as a Bean to customize the user name and password.
In fact, Spring Security is only concerned with reading; the storage can be implemented by the business system itself; It is equivalent to implementing only the interface UserDetailsService.
Spring Security has preset two common storage media implementations for us:
-
InMemoryUserDetailsManager, based on the realization of the memory
-
JdbcUserDetailsManager, database based implementation
InMemoryUserDetailsManager JdbcUserDetailsManager and implementing an interface UserDetailsManager, nature is populated UserDetails CRUD. We’ll start with UserDetails, then move on to the memory-based and database-based implementations, respectively.
UserDetails
UserDetails is an abstract interface to user information:
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
Copy the code
getUsername
Gets the user name.
getPassword
Get the password.
getAuthorities
Obtain permission, which can be simply defined as role name (string), used to implement role-based authorized access on the interface. For details, see the following section.
other
Gets whether the user is available, or whether the user/password is expired or locked.
Spring Security provides an implementation class of UserDetails, User, for instance representation of User information. In addition, User provides a way to build objects in Builder mode.
UserDetails user = User.builder()
.username("user")
.password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
.roles("USER")
.build();
Copy the code
username
Set the user name.
password
Spring Security does not recommend storing passwords in plain text. The password format is as follows:
{id}encodedPassword
Copy the code
Id is the id of the encryption algorithm, and encodedPassword is the character string after the password is encrypted. This section uses the encryption algorithm bcrypt as an example. For details, see Password Storage.
roles
Set roles. Multiple roles are supported.
Once the UserDetails instance is created, it can be stored and read using the concrete implementation of the UserDetailsManager.
In Memory
InMemoryUserDetailsManager is Spring Security for our UserDetailsManager based on memory implementation.
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { ... @Bean public UserDetailsManager users() { UserDetails user = User.builder() .username("userA") .password("{bcrypt}$2a$10$CrPsv1X3hM" + ".giwVZyNsrKuaRvpJZyGQycJg78xT7Dm68K4DWN/lxS") .roles("USER") .build(); InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); manager.createUser(user); return manager; }}Copy the code
-
Create user instance user with user name userA and password 123456 (encrypted using Bcrypt algorithm). Roles are required for authentication, but roles must be set, specified here as USER;
-
Create InMemoryUserDetailsManager instance manager;
-
Use the createUser method to store the user to manager; Equivalent to the user information stored in memory medium;
-
Return to the manager;
Using the @ Bean will InMemoryUserDetailsManager instance into Spring Security.
Create InMemoryUserDetailsManager instance, does not have to immediately call createUser add user information, Also available in other parts of the business system has injected InMemoryUserDetailsManager dynamic storage populated UserDetails instance.
Compile and start the application using the user name and password we created ourselves (userA/123456) to access the interface:
curl -H "Authorization: Basic dXNlckE6MTIzNDU2" http://localhost:9999
index
Copy the code
The user-defined user name and password based on the memory medium are valid, and the interface responds normally.
JDBC
JdbcUserDetailsManager is Spring Security for our UserDetailsManager based on database implementation, compared with InMemoryUserDetailsManager use slightly complicated, We need to create the data table and prepare the DataSource for the database connection. The creation of the JdbcUserDetailsManager instance depends on the DataSource.
JdbcUserDetailsManager can share a database data source instance with a business system. This article does not discuss data source configuration.
MySQL > create table clause;
create table users(
username varchar(50) not null primary key,
password varchar(500) not null,
enabled boolean not null
);
create table authorities (
username varchar(50) not null,
authority varchar(50) not null,
constraint fk_authorities_users foreign key(username) references users(username)
);
create unique index ix_auth_username on authorities (username,authority);
Copy the code
For other database statements, see User Schema.
JdbcUserDetailsManager instance creation and injection, except
-
Get the injected dataSource instance dataSource;
-
To create an instance, pass in the dataSource instance dataSource.
, the whole process is similar to InMemoryUserDetailsManager, no longer here.
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { ...... @Autowired private DataSource dataSource; @Bean public UserDetailsManager users() { UserDetails user = User.builder() .username("user") .password("{bcrypt}$2a$10$CrPsv1X3hM" + ".giwVZyNsrKuaRvpJZyGQycJg78xT7Dm68K4DWN/lxS") .roles("USER") .build(); JdbcUserDetailsManager manager = new JdbcUserDetailsManager(dataSource); manager.createUser(user); return manager; }}Copy the code
Obtain the injected JdbcUserDetailsManager instance from the service system to dynamically store the UserDetails instance.
Compile and start the application using the user name and password we created ourselves (userA/123456) to access the interface:
curl -H "Authorization: Basic dXNlckE6MTIzNDU2" http://localhost:9999
index
Copy the code
The user-defined user name and password based on the database media are valid, and the interface responds normally.
Spring Security authentication
Spring Security can provide role-based permission control:
-
Different users can belong to different roles
-
Different roles can access different interfaces
Assume that there are two roles, USER (common USER) and ADMIN (administrator),
The role USER can access the interface /hello/name,
The ADMIN role can access the interface /hello/world,
After authentication, all users can access the interface /.
We need to reset HttpSecurity as described above:
protected void configure(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authorize -> authorize .mvcMatchers("/hello/name").hasRole("USER") .mvcMatchers("/hello/world").hasRole("ADMIN") .anyRequest().authenticated()) .httpBasic(); }Copy the code
mvcMatchers(“/hello/name”).hasRole(“USER”)
Set the role USER to access interface /hello/name.
mvcMatchers(“/hello/world”).hasRole(“ADMIN”)
Set role ADMIN to access interface /hello/world.
anyRequest().authenticated()
You can access the interface only after other interfaces are authenticated.
MvcMatchers supports the use of wildcards.
Create users belonging to roles USER and ADMIN:
USER name: userA, password: 123456, role: USER
User name: userB, password: abcdef, role: ADMIN
@Bean
public UserDetailsManager users() {
UserDetails userA = User.builder()
.username("userA")
.password("{bcrypt}$2a$10$CrPsv1X3hM.giwVZyNsrKuaRvpJZyGQycJg78xT7Dm68K4DWN/lxS")
.roles("USER")
.build();
UserDetails userB = User.builder()
.username("userB")
.password("{bcrypt}$2a$10$PES8fUdtRrQ9OxLqf4CofOfcXBLQ3lkY2TSIcs1E9A0z2wECmZigG")
.roles("ADMIN")
.build();
JdbcUserDetailsManager manager = new JdbcUserDetailsManager(dataSource);
manager.createUser(userA);
manager.createUser(userB);
return manager;
}
Copy the code
For user userA:
Access the interface using the username and password of user userA / :
curl -H "Authorization: Basic dXNlckE6MTIzNDU2" http://localhost:9999
index
Copy the code
The user is authenticated and can be accessed normally.
Use the username and password of user userA to access the interface /hello/name:
curl -H "Authorization: Basic dXNlckE6MTIzNDU2" http://localhost:9999/hello/name
hello name
Copy the code
The authentication is successful and the access is normal.
Use user name and password to access interface /hello/world:
curl -H "Authorization: Basic dXNlckE6MTIzNDU2" http://localhost:9999/hello/world { "timestamp": "2022-01-10T13:11:18.032+00:00", "status": 403, "error": "Forbidden", "path": "/hello/world"}Copy the code
If the authentication succeeds, user userA does not belong to the ADMIN role and cannot be accessed.
Access the interface using the username and password of user userA / :
curl -H "Authorization: Basic dXNlckE6MTIzNDU2" http://localhost:9999
index
Copy the code
The user is authenticated and can be accessed normally.
For user userB:
Access the interface using the username and password of user userB / :
curl -H "Authorization: Basic dXNlckI6YWJjZGVm" http://localhost:9999
index
Copy the code
The user is authenticated and can be accessed normally.
Use userB’s username and password to access the interface /hello/world:
curl -H "Authorization: Basic dXNlckI6YWJjZGVm" http://localhost:9999/hello/world
hello world
Copy the code
The authentication is successful and the access is normal.
Use user name and password of userB to access interface /hello/name:
curl -H "Authorization: Basic dXNlckI6YWJjZGVm" http://localhost:9999/hello/name { "timestamp": "The 2022-01-10 T13: disguise. 461 + 00:00", "status" : 403, "error" : "who", "path" : "/ hello/name}"Copy the code
If the authentication succeeds, userB does not belong to the role USER and cannot be accessed.
This might be a bit strange, but in general we would expect an administrator to have full access to the interface /hello/name, so the administrator should also have access to the interface /hello/name. How do you do that?
Method 1: Set USER userB to have roles USER and ADMIN.
UserDetails userB = User.builder()
.username("userB")
.password("{bcrypt}$2a$10$PES8fUdtRrQ9OxLqf4CofOfcXBLQ3lkY2TSIcs1E9A0z2wECmZigG")
.roles("USER", "ADMIN")
.build();
Copy the code
This approach is a little less “elegant”.
Method 2: Set the role ADMIN to include USER.
Spring Security has a Hierarchical Roles feature that supports inclusion between Roles.
There are two things to pay special attention to when using this feature:
- authorizeRequests
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize ->
authorize
.mvcMatchers("/hello/name").hasRole("USER")
.mvcMatchers("/hello/world").hasRole("ADMIN")
.mvcMatchers("/").authenticated())
.httpBasic();
}
Copy the code
Above are using HttpSecurity authorizeHttpRequests method, the need to change to HttpSecurity. AuthorizeRequests method.
- RoleHierarchy
@Bean
RoleHierarchy hierarchy() {
RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
hierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER");
return hierarchy;
}
Copy the code
RoleHierarchy is used to define hierarchical relationships between roles as beans. Where “ROLE_” is the fixed prefix required by Spring Security.
Use userB’s username and password to access the interface /hello/name:
curl -H "Authorization: Basic dXNlckI6YWJjZGVm" http://localhost:9999/hello/name
hello name
Copy the code
The authentication is successful and the access is normal.
If the debug log level of Spring Security is enabled, you can see the following log output when accessing the interface:
From the roles [ROLE_ADMIN] one can reach [ROLE_USER, ROLE_ADMIN] in zero or more steps.
Copy the code
As you can see, Spring Security can infer from the role ADMIN that the USER actually has both USER and ADMIN roles.
Special instructions
The example in the Hierarchical Roles documentation is obviously wrong:
@Bean
AccessDecisionVoter hierarchyVoter() {
RoleHierarchy hierarchy = new RoleHierarchyImpl();
hierarchy.setHierarchy("ROLE_ADMIN > ROLE_STAFF\n" +
"ROLE_STAFF > ROLE_USER\n" +
"ROLE_USER > ROLE_GUEST");
return new RoleHierarcyVoter(hierarchy);
}
Copy the code
The method setHierarchy does not exist in interface RoleHierarchy. The method of combining authorizeRequests and RoleHierarchy mentioned above is based on the combination of Internet search and personal practice, which is only for reference.
In addition, authorizeHttpRequests with RoleHierarchy do not work, AuthorizeRequests and authorizeHttpRequests are identified by reference to Authorize HttpServletRequests with AuthorizationFilter and authorizeHttpRequests respectively The Authorize it with FilterSecurityInterceptor.
Before authentication, you must pass the authentication. The status code of the authentication failure is 401, and the status code of the authentication failure is 403.
Spring Security exception handler
Spring Security exceptions are classified into two types: authentication failure exceptions and authentication failure exceptions. When an exception occurs, the corresponding default exception handler is used to handle the exception, namely, authentication failure exception handler and authentication failure exception handler.
The default exception handler may be used depending on the authentication or authentication implementation mechanism used.
Authentication failure exception handler
Spring Security authentication failed
public interface AuthenticationEntryPoint {
void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException;
}
Copy the code
As mentioned earlier, when authentication fails, Spring Security uses the default authentication failure handler implementation to return:
{" timestamp ":" the 2022-01-10 T02:47:20. 820 + 00:00 ", "status" : 401, "error" : "Unauthorized", "path" : "/"}Copy the code
If you want to customize what is returned, you can do this by customizing the authentication failure handler:
AuthenticationEntryPoint authenticationEntryPoint() {
return (request, response, authException) -> response
.getWriter()
.print("401");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
...
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint());
}
Copy the code
AuthenticationEntryPoint () creates and returns a custom instance of authenticationEntryPoint; Among them, use the HttpServletResponse. GetWriter (). Print () write we want to return the content: 401.
HttpBasic (). AuthenticationEntryPoint (authenticationEntryPoint ()) to use our custom authenticationEntryPoint replace httpBasic default BasicAuthenticationEntryPoint.
Compiling and starting the application using incorrect username and password to access interface / :
curl -H "Authorization: Basic error" http://localhost:9999
401
Copy the code
Authentication failed, return with our custom content 401.
Authentication failed exception handler
Spring Security authentication failed
public interface AccessDeniedHandler {
void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException;
}
Copy the code
As mentioned earlier, when authentication fails, Spring Security uses the default authentication failure handler implementation to return:
{"timestamp": "2022-01-10T13:18:29.461+00:00", "status": 403, "error": "Forbidden", "path": "/hello/name"}Copy the code
If you want to customize what is returned, you can customize the authentication failure handler:
AccessDeniedHandler accessDeniedHandler() {
return (request, response, accessDeniedException) -> response
.getWriter()
.print("403");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
...
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint())
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler());
}
Copy the code
The process of customizing the authentication failure handler is similar to that of the authentication failure handler.
Use the username and password of user userA to access the interface /hello/world:
curl -H "Authorization: Basic dXNlckE6MTIzNDU2" http://localhost:9999/hello/world
403
Copy the code
Authentication failed, use our custom content 403 return.
Pay special attention to
ExceptionHandling () also has an authenticationEntryPoint() method; Use for HttpBasic exceptionHandling (.) authenticationEntryPoint () sets the custom authentication failed processor is not effective, need everybody to study specific reasons.
Spring Security custom authentication
Spring Security also provides a number of other Authentication Mechanisms. For details, see Authentication Mechanisms.
If we want to implement our own authentication mode, it is relatively simple. Spring Security is essentially a filter, and we can implement our own authentication filters and add them to Spring Security.
Filter preAuthenticatedFilter() {
return (servletRequest, servletResponse, filterChain) -> {
...
UserDetails user = User
.builder()
.username("xxx")
.password("xxx")
.roles("USER")
.build();
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(
user,
user.getPassword(),
user.getAuthorities());
SecurityContext context =
SecurityContextHolder.createEmptyContext();
context.setAuthentication(token);
SecurityContextHolder.setContext(context);
filterChain.doFilter(servletRequest, servletResponse);
};
}
Copy the code
Authentication filter core implementation process:
- Complete a custom authentication process (omitted) using the information in the Http request (servletRequest), where:
-
Check that the username and password in the request match
-
Check whether the Token in the request is valid
-
other
If the authentication succeeds, go to the next step. If the authentication fails, you can throw an exception or skip subsequent steps.
- Extract username from the Http request and load the UserDetailsService (omitted) using the injected UserDetailsService instance;
For simplicity, simulate creating a user information instance user; At this point, the USER has been authenticated, and the USER name and password can be set at will. In fact, only the role is required. We set the role of the authenticated USER to USER.
- Create a user authentication id;
Spring Security depend on internal Authentication. IsAuthenticated () to determine whether the user has certification, UsernamePasswordAuthenticationToken Authentication is a kind of specific implementation, need to pay attention to create an instance of construction methods and parameters, Inside the constructor is called Authentication. SetAuthenticated (true).
- Create and set the environment context SecurityContext;
Environmental context holds the user authentication identity: context. SetAuthentication (token).
Pay special attention to
Filterchain.dofilter (servletRequest, servletResponse); It has to be carried out.
To understand the concepts involved in Authentication filters, see Servlet Authentication Architecture for details.
Once the authentication filters are created, they can be added to Spring Security:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
......
.addFilterBefore(preAuthenticatedFilter(),
ExceptionTranslationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint())
.accessDeniedHandler(accessDeniedHandler());
}
Copy the code
According to our configuration, Spring Security will automatically assemble a chain of filters for us in a certain order, and complete authentication through several filters on this chain. We need to add a custom authentication filters to the right position, this chain this is selected location is in front of ExceptionTranslationFilter.
See Security Filters for the order of the filter chain.
ExceptionTranslationFilter can refer to the action of Handling Security Exceptions.
Pay special attention to
This section describes how to set a custom authentication failure exception handler and authentication failure exception handler when a custom authentication filter is used.
When we compile and start the application, we see that we can access the interface/and /hello/name directly without filling in any authentication information, because the emulated USER is authenticated and the role is USER; When accessing interface /hello/world, prompt 403 appears.
conclusion
Spring Security itself contains a lot of content, and the official documentation is not very clear about how to use each feature. Most of the time, we need to deepen our understanding by practicing as much as possible according to the documents, examples, source code and others’ sharing.