身份验证与授权
使用数据库配置用户身份验证,并为不同角色和路径定义访问控制规则。
身份验证与授权 是 CoddyKit 上的免费 Spring Boot 4 Complete Guide 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Spring Boot 4 Complete Guide 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Spring Boot 4 Complete Guide 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
AuthN vs. AuthZ
In security, we often talk about two key concepts: Authentication and Authorization.
- Authentication (AuthN) is about verifying who you are. Think of it like showing your ID to prove your identity.
- Authorization (AuthZ) is about determining what you are allowed to do once your identity is confirmed. This is like your ID granting you access to certain areas.
Database Auth Flow
Spring Security can use user details stored in a database to authenticate users. Instead of hardcoding users, we can connect to a real data source.
The process typically involves:
- A user attempts to log in.
- Spring Security fetches user details (username, password, roles) from your database.
- It verifies the password.
- If successful, the user is authenticated.
Fetching User Details
The core interface for retrieving user-specific data in Spring Security is UserDetailsService. You'll implement this to tell Spring how to find users in your database.
It has one method: loadUserByUsername(String username). This method returns a UserDetails object, which holds the user's username, password, and authorities (roles).
Custom UserDetailsService
Let's create a simple UserDetailsService. For now, we'll simulate fetching users from a list, but in a real app, this would query a database.
Notice we return a User object, which is a common implementation of UserDetails.
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.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@Service
public class MyUserDetailsService implements UserDetailsService {
private final Map<String, UserDetails> users = new HashMap<>();
public MyUserDetailsService(PasswordEncoder passwordEncoder) {
// Simulate users from a database
users.put("user", User.builder()
.username("user")
.password(passwordEncoder.encode("password"))
.roles("USER")
.build());
users.put("admin", User.builder()
.username("admin")
.password(passwordEncoder.encode("adminpass"))
.roles("ADMIN", "USER")
.build());
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails user = users.get(username);
if (user == null) {
throw new UsernameNotFoundException("User not found: " + username);
}
return user;
}
}Securing Passwords
Storing passwords as plain text is a major security risk. Spring Security requires you to use a PasswordEncoder to securely hash (encode) passwords.
The most common implementation is BCryptPasswordEncoder, which uses a strong hashing algorithm. You'll define this as a Spring bean.
Wiring Up Authentication
Now, let's wire our UserDetailsService and PasswordEncoder into Spring Security's configuration. We'll define a SecurityFilterChain bean.
This filter chain tells Spring how to handle requests, including authentication.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final UserDetailsService userDetailsService;
public SecurityConfig(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable()) // Disable CSRF for simplicity in this example
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated() // All other requests require authentication
)
.formLogin(form -> form.permitAll()); // Enable form login for browser access
return http.build();
}
}Full Auth Demo
Here's a complete Spring Boot application demonstrating basic authentication using our custom UserDetailsService and PasswordEncoder. Try accessing /hello after logging in!
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
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.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@SpringBootApplication
public class AuthApp {
public static void main(String[] args) {
SpringApplication.run(AuthApp.class, args);
}
}
@RestController
class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello, authenticated user!";
}
}
@Service
class MyUserDetailsService implements UserDetailsService {
private final Map<String, UserDetails> users = new HashMap<>();
public MyUserDetailsService(PasswordEncoder passwordEncoder) {
users.put("user", User.builder()
.username("user")
.password(passwordEncoder.encode("password"))
.roles("USER")
.build());
users.put("admin", User.builder()
.username("admin")
.password(passwordEncoder.encode("adminpass"))
.roles("ADMIN", "USER")
.build());
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails user = users.get(username);
if (user == null) {
throw new UsernameNotFoundException("User not found: " + username);
}
return user;
}
}
@Configuration
@EnableWebSecurity
class SecurityConfig {
private final UserDetailsService userDetailsService;
public SecurityConfig(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.formLogin(form -> form.permitAll());
return http.build();
}
}What You Can Do
Once a user is authenticated, authorization determines what resources or actions they are permitted to access. This is done by checking their assigned roles or authorities.
For example, an "ADMIN" user might access admin pages, while a "USER" user can only see their profile.
Securing Paths by Role
Spring Security allows you to define authorization rules directly in your SecurityFilterChain. You can protect specific URL patterns based on roles.
Key methods:
.requestMatchers("/admin/**").hasRole("ADMIN"): Only users with the 'ADMIN' role can access URLs under/admin/..requestMatchers("/user/**").hasAnyRole("USER", "ADMIN"): Users with 'USER' or 'ADMIN' role..anyRequest().authenticated(): All other requests need any authenticated user.
AuthZ in Action
Let's add authorization rules to our previous example. Try logging in as 'user' (password: 'password') and 'admin' (password: 'adminpass') and access the different endpoints.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
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.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@SpringBootApplication
public class AuthZApp {
public static void main(String[] args) {
SpringApplication.run(AuthZApp.class, args);
}
}
@RestController
class SecuredController {
@GetMapping("/public")
public String publicPage() {
return "This is a public page.";
}
@GetMapping("/user/profile")
public String userProfile() {
return "Welcome, user! This is your profile.";
}
@GetMapping("/admin/dashboard")
public String adminDashboard() {
return "Welcome, admin! This is the admin dashboard.";
}
}
@Service
class MyUserDetailsService implements UserDetailsService {
private final Map<String, UserDetails> users = new HashMap<>();
public MyUserDetailsService(PasswordEncoder passwordEncoder) {
users.put("user", User.builder()
.username("user")
.password(passwordEncoder.encode("password"))
.roles("USER")
.build());
users.put("admin", User.builder()
.username("admin")
.password(passwordEncoder.encode("adminpass"))
.roles("ADMIN", "USER")
.build());
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails user = users.get(username);
if (user == null) {
throw new UsernameNotFoundException("User not found: " + username);
}
return user;
}
}
@Configuration
@EnableWebSecurity
class SecurityConfig {
private final UserDetailsService userDetailsService;
public SecurityConfig(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/public").permitAll() // Public access
.requestMatchers("/user/**").hasRole("USER") // Only USER role
.requestMatchers("/admin/**").hasRole("ADMIN") // Only ADMIN role
.anyRequest().authenticated() // All others need authentication
)
.formLogin(form -> form.permitAll());
return http.build();
}
}Quick Check
Review the concepts of authentication and authorization, and the components involved.
Recap & Next Steps
Great job! In this lesson, you learned to:
- Distinguish between Authentication and Authorization.
- Understand how Spring Security uses databases for authentication.
- Implement a custom
UserDetailsServiceto fetch user data. - Use
PasswordEncoderto secure user passwords. - Configure URL-based authorization rules using roles.
Next, you'll explore more advanced security features like JWT-based authentication for stateless APIs!
常见问题解答
「身份验证与授权」课时是免费的吗?
是的 — 「身份验证与授权」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Spring Boot 4 Complete Guide 课程的其余内容,请升级到 CoddyKit PRO。 Spring Boot 4 Complete Guide 课程共包含 4 节课。
「身份验证与授权」这节课中我会学到什么?
使用数据库配置用户身份验证,并为不同角色和路径定义访问控制规则。 你通过在浏览器中直接运行的动手代码来练习 Spring Boot 4 Complete Guide,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Spring Boot 4 Complete Guide 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Spring Boot 4 Complete Guide 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「身份验证与授权」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Spring Boot 4 Complete Guide 课中编写并运行代码吗?
能。每节 Spring Boot 4 Complete Guide 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。