使用 Spring Security 进行身份验证
集成 Spring Security 保护 GraphQL 端点,并管理用户身份验证。
使用 Spring Security 进行身份验证 是 CoddyKit 上的免费 GraphQL APIs with Spring Boot 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 GraphQL APIs with Spring Boot 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 GraphQL APIs with Spring Boot 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
Why Auth for GraphQL?
Welcome! In this lesson, we'll integrate Spring Security with our GraphQL API. While GraphQL defines how to query and mutate data, it doesn't specify authentication or authorization.
Protecting your API is crucial to ensure only authorized users can access sensitive data and perform specific actions. Spring Security is a powerful and flexible framework for this.
Spring Security Basics
Spring Security is a comprehensive security framework for Spring applications. It provides authentication (verifying who you are) and authorization (what you're allowed to do).
- Authentication: Verifies a user's identity (e.g., username/password).
- Authorization: Determines if an authenticated user has permission to access a resource or perform an action.
We'll start with basic authentication, which is simple to set up for testing.
Add Security Dependency
First, we need to add the Spring Security starter dependency to our Spring Boot project. This brings in all the necessary components for security.
Add the following to your pom.xml file:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>Don't forget to also include your GraphQL starter, e.g., spring-boot-starter-graphql if you haven't already.
Configure Basic In-Memory Auth
For a quick start, Spring Security can use in-memory user details. We define a SecurityFilterChain bean to configure the HTTP security rules.
This setup creates two users: 'user' and 'admin', both with a password 'password' (encoded using BCrypt) and different roles.
package com.coddykit;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.httpBasic();
return http.build();
}
@Bean
public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
UserDetails user = User.builder()
.username("user")
.password(passwordEncoder.encode("password"))
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password(passwordEncoder.encode("password"))
.roles("ADMIN", "USER")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}Protecting GraphQL Paths
Now, let's explicitly secure our GraphQL endpoints. We want to make sure that only authenticated users can access the /graphql endpoint where our API lives.
We can modify the SecurityFilterChain to specify access rules for specific paths. For example, requiring the 'USER' role to access GraphQL.
package com.coddykit;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable()) // Disable CSRF for API (consider for production)
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/graphql/**", "/graphiql/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
)
.httpBasic();
return http.build();
}
@Bean
public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
UserDetails user = User.builder()
.username("user")
.password(passwordEncoder.encode("password"))
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password(passwordEncoder.encode("password"))
.roles("ADMIN", "USER")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}A Simple GraphQL Query
To test our security, let's create a very basic GraphQL query. This query will simply return a greeting message. We'll then try to access it with and without authentication.
This demonstrates a minimal GraphQL API that Spring Security will protect.
package com.coddykit;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.stereotype.Controller;
@Controller
public class GreetingController {
@QueryMapping
public String hello() {
return "Hello, secured GraphQL world!";
}
}Full Secure App Example
Here's a complete Spring Boot application integrating Spring Security with a GraphQL endpoint. Run this code, then try to access http://localhost:8080/graphql or /graphiql.
You should be prompted for a username and password (e.g., 'user'/'password'). Without them, access will be denied.
package com.coddykit;
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.graphql.data.method.annotation.QueryMapping;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.stereotype.Controller;
@SpringBootApplication
public class SecuredGraphQLApp {
public static void main(String[] args) {
SpringApplication.run(SecuredGraphQLApp.class, args);
}
}
@Controller
class GreetingController {
@QueryMapping
public String hello() {
return "Hello, secured GraphQL world!";
}
}
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/graphql/**", "/graphiql/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
)
.httpBasic();
return http.build();
}
@Bean
public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
UserDetails user = User.builder()
.username("user")
.password(passwordEncoder.encode("password"))
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password(passwordEncoder.encode("password"))
.roles("ADMIN", "USER")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}Custom UserDetailsService
In-memory users are great for development, but real applications need to load user data from a database or external service. This is where a custom UserDetailsService comes in.
You'd implement this interface to fetch user details (username, password, roles) based on the provided username. Spring Security then uses this to authenticate the user.
package com.coddykit;
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;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.crypto.password.PasswordEncoder;
@Service
public class MyCustomUserDetailsService implements UserDetailsService {
private final PasswordEncoder passwordEncoder;
public MyCustomUserDetailsService(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// In a real app, you'd fetch this from a DB
// For demonstration, let's hardcode a user
if ("coddy".equals(username)) {
return User.builder()
.username("coddy")
.password(passwordEncoder.encode("secret")) // Stored encoded password
.roles("USER")
.build();
} else {
throw new UsernameNotFoundException("User not found: " + username);
}
}
}Understanding PasswordEncoder
Never store passwords in plain text! PasswordEncoder is an interface for encoding and verifying passwords. BCryptPasswordEncoder is a strong, widely recommended implementation.
- Encoding: Transforms a plain-text password into a secure hash.
- Matching: Compares a raw password with an encoded one.
Spring Security automatically uses the PasswordEncoder bean you provide to handle password verification.
Security Integration Check
You've learned how to set up basic authentication for your GraphQL API. Let's test your understanding of the key components involved.
Lesson Recap: Secure Your API
Great job! You've learned how to integrate Spring Security to protect your GraphQL endpoints. We covered:
- The importance of authentication for GraphQL APIs.
- Adding the Spring Security dependency.
- Configuring a
SecurityFilterChainfor basic authentication. - Protecting specific GraphQL paths.
- Implementing a custom
UserDetailsServicefor real-world user management. - Using
PasswordEncoderfor secure password handling.
This lays the foundation for building robust and secure GraphQL APIs. Next, you'll explore authorization to control access to fields and operations!
常见问题解答
「使用 Spring Security 进行身份验证」课时是免费的吗?
是的 — 「使用 Spring Security 进行身份验证」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 GraphQL APIs with Spring Boot 课程的其余内容,请升级到 CoddyKit PRO。 GraphQL APIs with Spring Boot 课程共包含 4 节课。
「使用 Spring Security 进行身份验证」这节课中我会学到什么?
集成 Spring Security 保护 GraphQL 端点,并管理用户身份验证。 你通过在浏览器中直接运行的动手代码来练习 GraphQL APIs with Spring Boot,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 GraphQL APIs with Spring Boot 需要有经验吗?
无需任何先前经验。CoddyKit 上的 GraphQL APIs with Spring Boot 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「使用 Spring Security 进行身份验证」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 GraphQL APIs with Spring Boot 课中编写并运行代码吗?
能。每节 GraphQL APIs with Spring Boot 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- GraphQL 中的自定义错误处理
- 使用 Spring Security 进行身份验证
- 使用指令与上下文进行授权
- 速率限制与查询深度保护