1. Introduction to Spring Security
Spring Security is a security framework that provides declarative security protection for Spring-based applications. It provides a complete security solution that handles identity verification and authorization at the web request level and method invocation level. Because it is based on the Spring framework, Spring Security makes full use of dependency injection and aspect-oriented technologies.
Spring Security mainly solves security problems from two aspects:
-
Web request level : Use the filters in the Servlet specification to protect web requests and restrict access at the URL level.
-
Method invocation level : Use Spring AOP to protect method invocations, ensuring that only users with the appropriate permissions can access the secured methods.
If you want to have an in-depth understanding of the related concepts and implementation principles of Spring Security, you can click on the portal == " Basic Principles of Spring Security
Second, Spring Security's Web request-level security Demo
1. Create a new Spring project and add springsecurity dependencies to the pom.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2. Create a configuration class for Spring Security
package com.spring.security.springsecurity.config;
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;
@Configuration
@EnableWebSecurity //启用Web安全功能
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
//访问"/"和"/home"路径的请求都允许
.antMatchers("/", "/home","/staff","/staff/*")
.permitAll()
//而其他的请求都需要认证
.anyRequest()
.authenticated()
.and()
//修改Spring Security默认的登陆界面
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
//基于内存来存储用户信息
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("user").password(new BCryptPasswordEncoder().encode("123")).roles("USER").and()
.withUser("admin").password(new BCryptPasswordEncoder().encode("456")).roles("USER","ADMIN");
}
}
Code analysis:
@EnableWebSecurity annotation: Enable web security function (but it is useless by itself, Spring Security configuration class also needs to implement WebSecurityConfigurer or inherit WebSecurityConfigurerAdapter class, the latter is used in this Demo, because it is easier to configure and use).
@EnableWebMvcSecurity annotation: Deprecated in Spring 4.0.
WebSecurityConfigurerAdapter class: You can specify the details of Web security by overloading the three configure() methods of this class.
1. configure(WebSecurity): By overloading this method, the Filter chain of Spring Security can be configured.
2. configure(HttpSecurity): By overriding this method, you can configure how to protect requests through interceptors.
How to configure the protection path
method
can do
access(String)
Allows access if the given SpEL expression evaluates to true
anonymous()
Allow anonymous user access
authenticated()
Allow authenticated users to access
denyAll ()
Unconditionally deny all access
fullyAuthenticated()
If the user is fully authenticated (not authenticated by the Remember-me function), access is allowed
hasAnyAuthority(String…)
Allow access if the user has one of the given permissions
hasAnyRole(String…)
Allow access if the user has one of the given roles
hasAuthority(String)
Allow access if the user has the given permission
hasIpAddress(String)
Allow access if the request comes from the given IP address
hasRole(String)
Allow access if the user has the given role
not()
Negate the results of other access methods
allowAll()
unconditional access
rememberMe()
Allow access if the user is authenticated by the Remember-me feature
All SpEL expressions supported by Spring Security are as follows:
safe expression
Calculation results
authentication
User authentication object
denyAll
the result is always false
hasAnyRole(list of roles)
true if the user is granted any of the specified permissions
hasRole(role)
true if the user has been granted the specified permission
hasIpAddress(IP Adress)
User address
isAnonymous()
Is it an anonymous user
isAuthenticated()
not an anonymous user
isFullyAuthenticated
Not anonymous nor remember-me authentication
isRemberMe()
remember-me certification
allowAll
always true
principal
User main information object
3. configure(AuthenticationManagerBuilder): By overloading this method, the user-detail (user details) service can be configured.
How to configure user details
method
describe
accountExpired(boolean)
Defines whether the account has expired
accountLocked(boolean)
Defines if the account is locked
and()
for connection configuration
authorities(GrantedAuthority…)
Grant one or more permissions to a user
authorities(List)
Grant one or more permissions to a user
authorities(String…)
Grant one or more permissions to a user
credentialsExpired(boolean)
Defines if the credential has expired
disabled(boolean)
Defines whether the account has been disabled
password(String)
Define the user's password
roles(String…)
Grant a user one or more roles
There are three ways to store user information:
**1. Use memory-based user storage: **Through inMemoryAuthentication()
methods, we can enable, configure and arbitrarily populate memory-based user storage. And, we can call withUser()
the method to add a new user to the in-memory user store, the parameter of this method is username
. withUser()
The method returns UserDetailsManagerConfigurer.UserDetailsBuilder, which provides several methods for further configuring the user, including methods for setting the user's password password()
and granting one or more role permissions for a given user roles()
. Note that roles()
method is authorities()
a shorthand for method. roles()
The values given to the method are prefixed with a ROLE_
prefix and granted to the user as a permission. Therefore, the authority of the appeal code user is: ROLE_USER,ROLE_ADMIN。而
with the help of a passwordEncoder()
method to specify a password encoder (encoder), we can encrypt and store the user password.
**2. Authentication based on database tables: **User data is usually stored in a relational database and accessed through JDBC. To configure Spring Security to use a JDBC-backed user store, we can use the jdbcAuthentication()
method and configure its DataSource so that we can access the relational database.
**3. LDAP-based authentication: **In order for Spring Security to use LDAP-based authentication, we can use the ldapAuthentication()
method.
3. Controller layer code and front-end code
The SecurityController code is as follows:
package com.spring.security.springsecurity.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class SecurityController {
@GetMapping(value = {"/home","/"})
public String home(){
return "home";
}
@GetMapping(value = "/hello")
public String hello(){
return "hello";
}
@GetMapping(value = "/login")
public String login(){
return "login";
}
}
home.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Spring Security Example</title>
</head>
<body>
<h1>Welcome!</h1>
<p>Click <a th:href="@{/hello}">here</a> to see a greeting.</p>
</body>
</html>
hello.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Hello World!</title>
</head>
<body>
<h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
<form th:action="@{/logout}" method="post">
<input type="submit" value="Sign Out"/>
</form>
</body>
</html>
login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Spring Security Example </title>
</head>
<body>
<div th:if="${param.error}">
Invalid username and password.
</div>
<div th:if="${param.logout}">
You have been logged out.
</div>
<form th:action="@{/login}" method="post">
<div><label> User Name : <input type="text" name="username"/> </label></div>
<div><label> Password: <input type="password" name="password"/> </label></div>
<div><input type="submit" value="Sign In"/></div>
</form>
</body>
</html>
4. Operation results and analysis
When we visit localhost:8080/hello directly, the page will jump to http://localhost:8080/login , this is because the SecurityConfig class configures only requests for "/" and "/home" paths without logging in can be accessed, while other requests require authentication. (Note: I have changed the port here to 8081 because I have made adjustments. You can access port 8080 as usual.)
At this point, we enter in the login interface, we configure the user name and password based on memory storage in SecurityConfig, and we will successfully jump to the hello interface
Three, Spring Security's method invocation level security Demo
Spring Security provides three different security annotations:
- Spring Security's own @Secured annotation;
- @RolesAllowed annotation of JSR-250;
- Expression-driven annotations, including @PreAuthorize, @PostAuthorize, @PreFilter, and @PostFilter.
annotation
describe
@PreAuthorize
Restrict access to methods based on the evaluation of expressions before the method invocation
@PostAuthorize
Method invocation is allowed, but a security exception will be thrown if the expression evaluates to false
@PostFilter
Method invocation is allowed, but the result of the method must be filtered by expression
@PreFilter
Method calls are allowed, but input values must be filtered before entering the method
1. Enable annotation-based method security
In Spring, if you want to enable annotation-based method security, the key is to use @EnableGlobalMethodSecurity on the configuration class, such as:
package com.spring.security.springsecurity.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,jsr250Enabled = true,prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
}
Here we set securedEnabled = true, then Spring will create a pointcut and prevent methods annotated with @Secured from entering the aspect. Similarly, jsr250Enabled = true and prePostEnabled = true, respectively enable @RolesAllowed and expression-driven annotations.
After configuring the MethodSecurityConfig class at this point, we can add a method to the SecurityController based on the appeal code:
@GetMapping(value = "/admin")
@Secured("ROLE_ADMIN")
public String admin(){
return "admin";
}
The admin.html interface is as follows:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>admin</title>
</head>
<body>
<h1>admin page</h1>
</body>
</html>
At this point, how do we access the http://localhost:8081/admin path as the user user, and a 403 error will occur.
If you access the http://localhost:8081/admin path as the admin user, the following interface will be displayed, indicating successful access.