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

yizhihongxing

下面是“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日

相关文章

  • 如何使用eclipse搭建maven多module项目(构建父子项目)

    下面是如何使用Eclipse搭建Maven多module项目(构建父子项目)的详细步骤: 1. 创建Maven父项目 在Eclipse中,选择菜单“File” -> “New” -> “Other”,选择“Maven” -> “Maven Project”,点击“Next”。 在“New Maven Project”向导中,选择“Creat…

    Java 2023年5月20日
    00
  • 详解Java 中的函数式接口

    详解Java 中的函数式接口 函数式编程作为现代编程语言的一种编程范式,使用的越来越广泛。而Java 8以后,也开始支持函数式编程。函数式编程有一个非常重要的概念——函数式接口。本文将通过以下几个方面详细讲解Java中的函数式接口。 什么是函数式接口? 函数式接口是指仅有一个抽象方法的接口。函数式接口是函数式编程的核心。 Java 的Lambda 表达式、方…

    Java 2023年5月26日
    00
  • java基础之String知识总结

    Java基础之String知识总结 String的定义与特点 String是Java语言中的一种引用类型,其特点是不可变。 在Java中,所有的字符串都被封装在String对象中,可以通过双引号来创建字符串对象,例如: String str = "Hello World"; String的常用方法 length() 用于获取字符串的长度,…

    Java 2023年5月26日
    00
  • 关于在Java中反转数组的4种详细方法

    针对“关于在Java中反转数组的4种详细方法”,我可以给出以下几种方式: 1. 使用for循环逆序遍历数组 public static void reverseWithForLoop(int[] arr) { int len = arr.length; for (int i = len – 1; i >= len / 2; i–) { int tem…

    Java 2023年5月26日
    00
  • SpringBoot使用JdbcTemplate访问操作数据库基本用法

    SpringBoot使用JdbcTemplate访问操作数据库基本用法 简介 JdbcTemplate 是 Spring 框架提供的一种基于 JDBC 的访问数据库的工具,使用它可以简化 JDBC 的开发流程和操作,减少大量模板式代码的编写。结合 SpringBoot 使用 JdbcTemplate 可以更加方便地访问和操作数据库。 Maven 依赖 在 S…

    Java 2023年5月20日
    00
  • Java8 日期、时间操作代码

    Java8引入了新的时间日期API,该API提供了更好的日期时间处理方式,包括易于格式化和解析日期时间、更好的时区支持和可扩展性,下面是Java 8日期和时间操作的完整攻略: 获取当前日期和时间 通过使用Java 8日期API,我们可以轻松地获取当前日期和时间。以下是获取当前日期和时间的代码示例: LocalDateTime now = LocalDateT…

    Java 2023年5月20日
    00
  • 基于自定义校验注解(controller、method、(groups)分组的使用)

    基于自定义校验注解的使用可以提高代码的可读性和可维护性,可以定义自己的业务规则并在控制器中进行验证,从而更好地保障数据的安全性。下面给出一个完整的攻略,包括自定义注解的编写、控制器中的使用、注解的分组以及两个示例。 编写自定义注解 自定义注解应该使用@Target和@Retention注解对其进行标记,使其可以被正确地应用到需要校验的方法上。下面是一个简单的…

    Java 2023年5月20日
    00
  • java中接口(interface)及使用方法示例

    下面详细讲解“Java中接口(interface)及使用方法示例”的完整攻略。 一、接口的概念 在 Java 中,接口就是一个抽象类型,它只包含抽象方法的定义。接口定义了一组方法,但没有给出方法的实现。其主要作用是描述类应该具有的功能,而不具体地提供实现。 接口定义的格式如下: public interface 接口名称 { // 抽象方法的定义 } 接口内…

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