SpringBoot+SpringSecurity实现基于真实数据的授权认证

下面是“SpringBoot+SpringSecurity实现基于真实数据的授权认证”的完整攻略:

1. 简介

Spring Security 是 Spring 社区中安全领域的一部分,它提供了强大且可高度定制化的身份验证和授权框架。同时,Spring Security 还可以与 Spring 的其他模块轻松集成,比如:Spring Boot、Spring MVC、Spring Data等。

本文主要针对如何使用 Spring Boot 和 Spring Security 实现基于真实数据的授权认证,并分别给出了两个示例。

2. 实现步骤

下面是具体的实现步骤:

2.1 引入 Spring Security 依赖

在 pom.xml 文件中添加如下依赖:

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

2.2 配置 Spring Security

在 Spring Boot 的配置文件 application.properties 或 application.yml 中添加如下配置:

# 禁用 Spring Security 自带的 CSRF 防护
security.enable-csrf=false

# 配置用户信息存储格式
spring.security.user.name=admin
spring.security.user.password=admin123
spring.security.user.roles=admin, user

该配置文件设置了禁用 CSRF 防护功能,指定用户名和密码以及用户角色。

2.3 编写授权接口

在 Spring Boot 项目中,通过注解来标记相应的请求,以此来实现对指定接口的授权访问控制。

例如,下面的代码中通过 @Secured 注解来标记了一个需要 admin 角色才能访问的 API 接口。

@RestController
@RequestMapping("/api")
public class DemoController {

    @Secured({"ROLE_ADMIN"})
    @GetMapping("/hello")
    public String hello() {
        return "Hello World!";
    }
}

2.4 测试授权

在浏览器中输入 http://localhost:8080/api/hello ,此时会跳转到 Spring Security 的默认登录页面。输入用户名和密码之后,如果用户拥有 admin 角色,则可以成功访问 hello 接口。

3. 示例一

本示例中,我们将通过 JPA 存储用户信息和权限信息,以此来实现基于真实数据的授权认证。

3.1 引入 JPA 依赖

在 pom.xml 文件中添加如下依赖:

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

3.2 创建用户信息和权限信息实体类

创建用户信息实体类 User 和权限信息实体类 Permission。

@Entity
@Table(name = "user")
public class User implements UserDetails {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;

    private String password;

    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinTable(name = "user_permission",
            joinColumns = {@JoinColumn(name = "user_id")},
            inverseJoinColumns = {@JoinColumn(name = "permission_id")})
    private Set<Permission> permissions;

    // 省略 getter 和 setter
}

@Entity
@Table(name = "permission")
public class Permission implements GrantedAuthority {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    // 省略 getter 和 setter
}

注:User 类实现了 UserDetails 接口,而 Permission 类实现了 GrantedAuthority 接口,这样可以同时存储权限和用户信息,方便后续认证。

3.3 创建 UserRepository 和 PermissionRepository 接口

创建 UserRepository 和 PermissionRepository 两个接口来继承 JpaRepository 接口,以此来实现对用户和权限信息的数据 CRUD 操作。

public interface UserRepository extends JpaRepository<User, Long> {

    User findByUsername(String username);
}

public interface PermissionRepository extends JpaRepository<Permission, Long> {

    Permission findByName(String name);
}

在 UserRepository 接口中定义了根据用户名来查找用户的方法,PermissionRepository 接口中定义了根据权限名来查找权限的方法。

3.4 实现 UserDetailsService 接口

在 Spring Security 中,UserDetailsService 接口用来从数据库中根据用户名查询用户信息,并返回一个 UserDetails 对象,该对象包含了用户名、密码和用户所拥有的权限等信息。我们需要创建一个实现 UserDetailsService 接口的类 UserDetailsServiceImpl。

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("用户不存在");
        }

        return new User(user.getUsername(), user.getPassword(), user.getPermissions());
    }
}

在代码中,我们通过 UserRepository 接口来查询数据库中的用户信息,然后将查询结果封装成一个 UserDetails 对象并返回。

3.5 配置 Spring Security

在 Spring Boot 的配置文件 application.yml 中增加如下配置:

spring.jpa.hibernate.ddl-auto=create

spring.datasource.url=jdbc:mysql://localhost:3306/demo?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect

spring.jpa.show-sql=true

security.enable-csrf=false

spring.security.user.name=admin
spring.security.user.password=admin123
spring.security.user.roles=admin

spring.profiles.active=jpa

该配置文件中定义了数据库相关信息和 JPA 相关配置,配置文件中还指定了用户名、密码和用户角色信息。

3.6 编写授权接口

在 Spring Boot 项目中,可以通过注解来标记相应的请求,以此来实现对指定接口的授权访问控制。例如,下面的代码中通过 @PreAuthorize 注解来标记只有具有 admin 角色和 USER_VIEW 权限的用户才能访问接口。

@RestController
@RequestMapping("/api")
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PermissionRepository permissionRepository;

    @PreAuthorize("hasRole('ADMIN') and hasPermission(null,'USER_VIEW')")
    @GetMapping("/users")
    public List<User> getUsers() {
        return userRepository.findAll();
    }

    @PreAuthorize("hasRole('ADMIN') and hasPermission(null,'USER_DELETE')")
    @DeleteMapping("/users/{id}")
    public void deleteUser(@PathVariable Long id) {
        userRepository.deleteById(id);
    }
}

3.7 测试授权

在浏览器中输入 http://localhost:8080/api/users ,此时会跳转到 Spring Security 的默认登录页面。输入用户名和密码之后,如果用户拥有 admin 角色并且具有 USER_VIEW 权限,则可以查看所有用户信息。另外,删除用户需要具备 USER_DELETE 权限。

4. 示例二

本示例中,我们将通过 JWT 存储用户信息和权限信息,以此来实现基于真实数据的授权认证。

4.1 引入依赖

在 pom.xml 文件中添加如下依赖:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.6.0</version>
</dependency>

4.2 创建 JWT 相关类

创建 JwtUtils 和 JwtAuthenticationToken 类。

JwtUtils 类用于生成和解析 JWT,如下所示。

@Component
public class JwtUtils {

    @Value("${jwt.secret}")
    private String secret;

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("sub", userDetails.getUsername());
        claims.put("created", new Date());
        claims.put("roles", userDetails.getAuthorities());
        return Jwts.builder().setClaims(claims).setExpiration(new Date(System.currentTimeMillis() + 60 * 60 * 1000))
                .signWith(SignatureAlgorithm.HS512, secret).compact();
    }

    public String getUsernameFromToken(String token) {
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getSubject();
    }
}

JwtAuthenticationToken 类用于包装认证的 token。

public class JwtAuthenticationToken extends UsernamePasswordAuthenticationToken {

    private String token;

    public JwtAuthenticationToken(String token) {
        super(null, null);
        this.token = token;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }
}

4.3 实现 AuthenticationProvider 接口

我们需要创建一个实现 AuthenticationProvider 接口的类 JwtAuthenticationProvider。AuthenticationProvider 接口是 Spring Security 提供的一个核心接口,用于处理认证逻辑。

@Component
public class JwtAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private JwtUtils jwtUtils;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) authentication;
        String token = jwtAuthenticationToken.getToken();

        String username = jwtUtils.getUsernameFromToken(token);
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);

        if (jwtUtils.validateToken(token, userDetails)) {
            return new JwtAuthenticationToken(token, userDetails.getAuthorities());
        }

        throw new BadCredentialsException("Token 校验失败");
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return JwtAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

在代码中,我们通过调用 JwtUtils 类的 validateToken 方法来校验 token 的有效性。

4.4 配置 Spring Security

在 Spring Boot 的配置文件 application.yml 中增加如下配置:

security.enabled=false

jwt.secret=secret
jwt.header=Authorization
jwt.prefix=Bearer
jwt.expiration=3600
jwt.path=/api/authenticate

该配置文件中定义了 JWT 相关的配置信息。

4.5 编写认证接口

在 Spring Boot 项目中,可以通过注解来标记相应的请求,以此来实现对指定接口的授权访问控制。例如,下面的代码中通过使用 Spring Security 的 formLogin() 方法来指定认证 API 的路径。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthenticationProvider jwtAuthenticationProvider;

    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        http.csrf().disable().authorizeRequests().antMatchers("/api/authenticate").permitAll().anyRequest()
                .authenticated().and().formLogin().permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(jwtAuthenticationProvider);
    }
}

@RestController
@RequestMapping("/api")
public class AuthController {

    private static final String AUTHENTICATION_SUCCESSFUL = "认证成功";

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtUtils jwtUtils;

    @PostMapping("/authenticate")
    public ResponseEntity<TokenDto> createAuthenticationToken(@RequestBody AuthenticateDto authenticationRequest) {
        Authentication authentication = authenticationManager.authenticate(
                new JwtAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword()));

        SecurityContextHolder.getContext().setAuthentication(authentication);

        UserDetails userDetails = (UserDetails) authentication.getPrincipal();

        final String token = jwtUtils.generateToken(userDetails);

        return ResponseEntity.ok(new TokenDto(token, AUTHENTICATION_SUCCESSFUL));
    }
}

在代码中,我们使用 Spring Security 的 formLogin() 方法指定了认证 API 的路径,同时将 JwtAuthenticationFilter 过滤器添加到了 Spring Security 的过滤器链中。

4.6 编写授权接口

在 Spring Boot 项目中,可以通过注解来标记相应的请求,以此来实现对指定接口的授权访问控制。例如,下面的代码中通过 @PreAuthorize 注解来标记只有具有 admin 角色和 USER_VIEW 权限的用户才能访问接口。

@RestController
@RequestMapping("/api")
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @PreAuthorize("hasRole('ADMIN') and hasPermission(null,'USER_VIEW')")
    @GetMapping("/users")
    public List<User> getUsers() {
        return userRepository.findAll();
    }

    @PreAuthorize("hasRole('ADMIN') and hasPermission(null,'USER_DELETE')")
    @DeleteMapping("/users/{id}")
    public void deleteUser(@PathVariable Long id) {
        userRepository.deleteById(id);
    }
}

4.7 测试授权

在浏览器中输入 http://localhost:8080/api/users ,此时会跳转到登录页面。输入用户名和密码之后,如果用户拥有 admin 角色并且具有 USER_VIEW 权限,则可以查看所有用户信息。另外,删除用户需要具备 USER_DELETE 权限。输入用户名和密码之后,会返回一个 JWT Token。在接下来的每个请求中都需要在请求头中添加该 Token 信息,这样后台才能验证该 Token 的有效性。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot+SpringSecurity实现基于真实数据的授权认证 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • Maven pom.xml 添加本地jar包依赖以及打包方法

    下面是Maven pom.xml添加本地jar包依赖以及打包方法的完整攻略。 1. 添加本地Jar包依赖 1.1 使用systemPath属性添加本地Jar包 在Maven pom.xml文件的dependencies节点下添加如下代码: <dependency> <groupId>local</groupId> <…

    Java 2023年5月19日
    00
  • SpringBoot嵌入式Web容器原理与使用介绍

    SpringBoot嵌入式Web容器原理与使用介绍 什么是SpringBoot嵌入式Web容器 SpringBoot是基于Spring框架的一个快速开发框架,它内置了多种Web容器,可以很方便地选择使用嵌入式Web容器,而不需要依赖外置的Web容器。SpringBoot嵌入式Web容器是指将Web容器嵌入到应用程序中,将应用程序打成可执行的jar或war包后…

    Java 2023年5月20日
    00
  • springboot-jpa的实现操作

    下面是对“springboot-jpa的实现操作”的完整攻略。 一、概述 Spring Boot是一个快速开发框架,提供了很多快捷而且方便的配置方式,其中对JPA的支持也是非常好的。本攻略将介绍如何使用Spring Boot进行JPA的实现操作。 二、前提条件 在继续之前,你需要确保以下条件已满足: 你已经掌握了基本的Spring Boot使用; 你已经安装…

    Java 2023年5月20日
    00
  • Mybatis通过数据库表自动生成实体类和xml映射文件

    “Mybatis通过数据库表自动生成实体类和xml映射文件”的完整攻略主要包括以下步骤:使用Mybatis Generator插件生成实体类和xml映射文件,配置Mybatis Generator插件,使用命令行或maven命令运行生成器。 使用Mybatis Generator插件生成实体类和xml映射文件 Mybatis Generator是一个能够根据…

    Java 2023年5月20日
    00
  • Maven构建生命周期详细介绍

    介绍Maven构建生命周期之前,首先需要了解一下Maven中的概念: POM(Parent Object Model): Maven项目的核心文件,包含了项目的基本信息和配置信息。 Artifact(构件):是一个独立的、可重用的软件组件,包括代码和其所依赖的库、配置文件等。 Dependency(依赖):描述当前项目所依赖的其他构件,用于下载构件到本地仓库…

    Java 2023年5月20日
    00
  • 一文详解JAVA中InputStreamReader流

    一、概述 InputStreamReader是Java中的输入流,是字符流与字节流之间的桥梁。它将字节流转换为字符流,以便于阅读和操作。 二、用法 InputStreamReader的用法非常简单,只需要创建一个InputStreamReader实例,并且为其传入一个输入流,然后就可以操作输入流中的字符了。 示例代码如下: try { InputStream…

    Java 2023年5月20日
    00
  • maven打包如何指定jdk的版本

    Maven是一个非常流行的Java项目管理和构建工具。在使用Maven进行代码打包时,我们经常遇到需要指定JDK版本的情况。接下来,我将详细介绍在Maven中如何指定JDK版本。 方式一:在pom.xml文件中指定JDK版本 可以在Maven项目的pom.xml文件中指定JDK版本,这样在构建项目时就可以使用特定版本的JDK。可以使用以下示例代码来指定JDK…

    Java 2023年5月19日
    00
  • 学会Java字节码指令,成为技术大佬

    学会Java字节码指令,成为技术大佬 什么是Java字节码指令? Java字节码指令是JVM(Java虚拟机)的指令集,用于执行Java程序。它是一种面向堆栈的指令集,包含了各种类型的指令,例如控制流指令、算数指令、类型转换指令等。学会Java字节码指令,对于深入理解Java语言、Java虚拟机以及性能优化都非常有帮助。 学习Java字节码指令的攻略 1. …

    Java 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部