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日

相关文章

  • 详细分析JAVA加解密算法

    详细分析JAVA加解密算法 在JAVA中,常用的加解密算法包括对称加密算法、非对称加密算法和HASH算法。在这里,我们将详细分析这些加解密算法的实现过程以及相关代码示例。 对称加密算法 对称加密算法使用同一个密钥进行加密和解密。其加密过程简单、高效,但密钥的共享是该算法的重要瓶颈。 在JAVA中,常用的对称加密算法包括DES、3DES、AES和Blowfis…

    Java 2023年5月19日
    00
  • java取两个字符串的最大交集

    Java取两个字符串的最大交集的算法可以通过动态规划(Dynamic Programming)来实现,其中最长公共子串(Longest Common Substring, LCS)就是该问题的一个特例。 以下是完整的攻略: 步骤1:定义状态 定义一个二维数组 dp[i][j],表示字符串 a 的前 i 个字符和字符串 b 的前 j 个字符的最长公共子串长度。…

    Java 2023年5月27日
    00
  • Java中的==使用方法详解

    Java中的==使用方法详解 在Java中,==是一种用于比较两个变量是否相等的运算符,但是它的使用方法有一些需要注意的地方。 关于==和equals()方法 在Java中,==用于比较两个变量的引用地址是否相等,即它们是否指向同一块内存地址。而equals()方法通常被用来比较两个对象的内容是否相等。 示例1: String str1 = "he…

    Java 2023年5月20日
    00
  • Java中BigInteger类的使用方法详解(全网最新)

    Java中BigInteger类的使用方法详解 简介 在 Java 中对于 数值类型 的定义都是有范围的,而当我们需要用到超出这个范围的大整数时,就需要 BigInteger 类了。BigInteger 类属于 java.math 包,可以让我们处理任意长度的整数。 基本使用 1. 创建 BigInteger 对象 我们可以直接使用不同的构造函数或者将字符串…

    Java 2023年5月26日
    00
  • shell脚本自动化创建虚拟机的基本配置之tomcat–mysql–jdk–maven

    下面是关于”shell脚本自动化创建虚拟机的基本配置之tomcat–mysql–jdk–maven”的完整攻略。 准备工作 在开始创建虚拟机之前,需要先完成以下准备工作: 选择合适的虚拟化软件,如VirtualBox,并安装在本地操作系统中。 准备虚拟机的镜像文件,如CentOS 7,下载好后可以在VirtualBox中导入镜像。 创建虚拟机 使用Vi…

    Java 2023年5月20日
    00
  • 使用JDBC实现数据访问对象层(DAO)代码示例

    下面是使用JDBC实现数据访问对象层(DAO)代码示例的完整攻略: 1. JDBC DAO层的基本结构 在实现JDBC DAO层之前,需要先确定DAO层的基本结构。一般来说,DAO层包括以下三个部分: DAO接口,用于定义数据的增删改查操作。 DAO实现类,用于实现DAO接口,提供具体的数据访问操作。 实体类,用于存储数据库中的数据,每个实体类对应一张数据表…

    Java 2023年5月26日
    00
  • jsp页面中EL表达式被当成字符串处理不显示值问题的解决方法

    当jsp页面中的EL表达式被当成字符串处理时,通常是因为在表达式中未添加适当的标识符。这种情况下,jsp引擎将认为该表达式是一个字符串,而不是一个有效的EL表达式。 为了解决这个问题,我们需要为EL表达式添加正确的标识符,以确保jsp引擎正确地解释它们。 下面是解决此问题的两种方法。 方法一:使用${}标识符 ${}是一个有效的EL表达式标识符,它可以用来表…

    Java 2023年6月15日
    00
  • Java利用Jackson轻松处理JSON序列化与反序列化

    下面是“Java利用Jackson轻松处理JSON序列化与反序列化”的完整攻略。 简介 在Java开发中,我们常常需要对JSON数据进行序列化和反序列化操作。JSON是一种轻量级的数据交换格式,常用于数据传递和存储。而Jackson是一款高效、灵活、功能强大的Java库,用于处理JSON数据。本文将介绍如何使用Jackson来进行JSON序列化和反序列化操作…

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