Spring Security 简单使用教程

举报
繁依Fanyi 发表于 2024/10/23 09:57:14 2024/10/23
【摘要】 Spring Security 是一个强大的、安全性框架,用于保护 Spring 应用程序。本文将详细介绍如何在一个 Spring Boot 项目中使用 Spring Security,从基础配置到自定义安全需求。 创建 Spring Boot 项目首先,我们需要创建一个 Spring Boot 项目。可以通过 Spring Initializr 快速生成项目。访问 Spring Initi...

Spring Security 是一个强大的、安全性框架,用于保护 Spring 应用程序。本文将详细介绍如何在一个 Spring Boot 项目中使用 Spring Security,从基础配置到自定义安全需求。

创建 Spring Boot 项目

首先,我们需要创建一个 Spring Boot 项目。可以通过 Spring Initializr 快速生成项目。

  1. 访问 Spring Initializr.
  2. 选择如下选项:
    • Project: Gradle Project 或 Maven Project
    • Language: Java
    • Spring Boot: 最新稳定版本
  3. 添加依赖:
    • Spring Web
    • Spring Security
    • Spring Data JPA(用于后续数据库操作)
    • H2 Database(用于演示)

生成并下载项目,解压后使用你喜欢的 IDE(如 IntelliJ IDEA 或 Eclipse)打开。

引入 Spring Security 依赖

如果你通过 Spring Initializr 创建了项目,Spring Security 依赖应该已经包括在内。如果没有,可以手动添加依赖:

Maven:

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

Gradle:

implementation 'org.springframework.boot:spring-boot-starter-security'

默认配置和基本认证

Spring Security 默认提供了基本的 HTTP Basic 认证。启动项目后,访问任意端点,你会被要求输入用户名和密码。

默认情况下,Spring Boot 会生成一个随机密码并打印在控制台。默认用户名是 user

Using generated security password: <generated-password>

自定义用户认证

让我们创建一个自定义的用户认证。首先,创建一个配置类来扩展 WebSecurityConfigurerAdapter

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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    @Override
    protected UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withDefaultPasswordEncoder()
                .username("user")
                .password("password")
                .roles("USER")
                .build());
        manager.createUser(User.withDefaultPasswordEncoder()
                .username("admin")
                .password("admin")
                .roles("ADMIN")
                .build());
        return manager;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasRole("USER")
                .antMatchers("/", "/home").permitAll()
                .and()
                .formLogin();
    }
}

基于数据库的用户认证

接下来,配置基于数据库的用户认证。首先,创建用户实体类和数据库表。

User 实体类:

import javax.persistence.*;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String password;
    private String role;

    // getters and setters
}

UserRepository 接口:

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
    User findByUsername(String username);
}

数据库初始化脚本:
src/main/resources 目录下创建 data.sql 文件,初始化一些数据。

INSERT INTO user (username, password, role) VALUES ('user', 'password', 'ROLE_USER');
INSERT INTO user (username, password, role) VALUES ('admin', 'admin', 'ROLE_ADMIN');

SecurityConfig 配置:

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.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
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;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserRepository userRepository;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected UserDetailsService userDetailsService() {
        return username -> {
            User user = userRepository.findByUsername(username);
            if (user == null) {
                throw new UsernameNotFoundException("User not found");
            }
            return org.springframework.security.core.userdetails.User
                    .withUsername(user.getUsername())
                    .password(passwordEncoder().encode(user.getPassword()))
                    .roles(user.getRole())
                    .build();
        };
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasRole("USER")
                .antMatchers("/", "/home").permitAll()
                .and()
                .formLogin();
    }
}

自定义登录页面

创建一个自定义的登录页面。首先,在 src/main/resources/templates 目录下创建一个 login.html 文件。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Login</title>
</head>
<body>
<h1>Login</h1>
<form th:action="@{/login}" method="post">
    <div>
        <label>Username:</label>
        <input type="text" name="username"/>
    </div>
    <div>
        <label>Password:</label>
        <input type="password" name="password"/>
    </div>
    <div>
        <button type="submit">Login</button>
    </div>
</form>
</body>
</html>

然后,在 SecurityConfig 中配置自定义登录页面。

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers("/admin/**").hasRole("ADMIN")
            .antMatchers("/user/**").hasRole("USER")
            .antMatchers("/", "/home").permitAll()
            .and()
            .formLogin()
            .loginPage("/login")
            .permitAll();
}

方法级别的安全性

Spring Security 还支持方法级别的安全性。我们可以在服务层使用注解来保护方法。

首先,启用全局方法安全性。在主应用程序类中添加 @EnableGlobalMethodSecurity(prePostEnabled = true) 注解。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;

@SpringBootApplication
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

然后,在服务层方法上使用 @PreAuthorize 注解。

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

@Service
public class MyService {

    @PreAuthorize("hasRole('ROLE_ADMIN')")
    public void adminMethod() {
        // admin only logic
    }

    @PreAuthorize("hasRole('ROLE_USER')")
    public void userMethod() {
        // user only logic
    }
}

处理跨站请求伪造(CSRF)

Spring Security 默认启用了 CSRF 保护。在开发和测试过程中,我们可能需要禁用它。在 SecurityConfig 中进行配置。

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/admin/**").hasRole("ADMIN")
            .antMatchers("/user/**").hasRole("USER")
            .antMatchers("/", "/home").permitAll()
            .and()
            .formLogin()
            .loginPage("/login")
            .permitAll();
}

日志记录和异常处理

我们可以通过自定义 AuthenticationEntryPointAccessDeniedHandler 来处理认证和授权异常。

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;


import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    }
}

@Component
public class CustomAccessDeniedHandler extends AccessDeniedHandlerImpl {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
        response.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden");
    }
}

SecurityConfig 中配置这些组件。

@Autowired
private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

@Autowired
private CustomAccessDeniedHandler customAccessDeniedHandler;

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/admin/**").hasRole("ADMIN")
            .antMatchers("/user/**").hasRole("USER")
            .antMatchers("/", "/home").permitAll()
            .and()
            .formLogin()
            .loginPage("/login")
            .permitAll()
            .and()
            .exceptionHandling()
            .authenticationEntryPoint(customAuthenticationEntryPoint)
            .accessDeniedHandler(customAccessDeniedHandler);
}

总结

通过这篇详细的教程,我们已经学习了如何在 Spring Boot 项目中集成 Spring Security,包括基本配置、自定义用户认证、基于数据库的用户认证、自定义登录页面、方法级别的安全性、CSRF 保护以及日志记录和异常处理。Spring Security 提供了强大的功能和灵活的配置选项,使我们能够根据需求来保护应用程序的安全。

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。