Spring Boot Spring Security JWT 实现简单的 RESTful API 权限控制

梦境旅人 2024-03-24 ⋅ 71 阅读

介绍

在开发 web 应用程序时,安全权限控制是非常重要的一项功能。使用 Spring Boot、Spring Security 和 JWT(JSON Web Token)可以帮助我们实现简单且强大的 RESTful API 权限控制。

环境准备

首先,确保你的开发环境配置好了以下组件:

  • JDK 1.8 或以上版本
  • Maven 3.x 或以上版本

创建 Spring Boot 项目

使用 Maven 创建一个新的 Spring Boot 项目,执行以下命令:

mvn archetype:generate -DgroupId=com.example -DartifactId=spring-boot-security-jwt-demo -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

添加依赖

在项目的 pom.xml 文件中添加以下依赖:

<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

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

    <!-- JWT -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.11.2</version>
    </dependency>
</dependencies>

配置 Spring Security

在 Spring Boot 项目的 application.properties 文件中添加以下配置:

# 禁用 security 默认的 http basic 认证方式
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration

然后,创建一个 SecurityConfig 类,配置 Spring Security:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
                .antMatchers(HttpMethod.POST, "/api/authenticate").permitAll()
                .anyRequest().authenticated()
                .and()
            .addFilter(new JwtAuthenticationFilter(authenticationManager()))
            .addFilter(new JwtAuthorizationFilter(authenticationManager()))
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
    }

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

上面的配置禁用了 Spring Security 的默认 http basic 认证方式,并指定了登录接口 /api/authenticate 不需要认证即可访问。

定义用户服务

创建一个 UserService 接口,并实现 UserDetailsService

@Service
public class UserService 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(username);
        }
        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), new ArrayList<>());
    }
}

定义数据模型

创建一个 User 实体类来表示用户对象,并创建一个 UserRepository 接口继承自 JpaRepository

@Entity
public class User {

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

    // getters and setters
}

public interface UserRepository extends JpaRepository<User, Long> {

    User findByUsername(String username);
}

实现认证和授权过滤器

创建一个 JwtAuthenticationFilter 类来处理认证逻辑:

public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private AuthenticationManager authenticationManager;

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
        setFilterProcessesUrl("/api/authenticate");
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        try {
            User user = new ObjectMapper().readValue(request.getInputStream(), User.class);

            return authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), new ArrayList<>())
            );
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        String token = Jwts.builder()
                .setSubject(((User) authResult.getPrincipal()).getUsername())
                .claim("authorities", new ArrayList<>())
                .setExpiration(new Date(System.currentTimeMillis() + 864000000))
                .signWith(SignatureAlgorithm.HS512, "SecretKeyToGenJWTs".getBytes())
                .compact();
        response.addHeader("Authorization", "Bearer " + token);
    }
}

创建一个 JwtAuthorizationFilter 类来处理授权逻辑:

public class JwtAuthorizationFilter extends BasicAuthenticationFilter {

    public JwtAuthorizationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String header = request.getHeader("Authorization");

        if (header == null || !header.startsWith("Bearer ")) {
            chain.doFilter(request, response);
            return;
        }

        UsernamePasswordAuthenticationToken authentication = getAuthentication(request);

        SecurityContextHolder.getContext().setAuthentication(authentication);
        chain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        String token = request.getHeader("Authorization");
        if (token != null) {
            String user = Jwts.parser()
                    .setSigningKey("SecretKeyToGenJWTs".getBytes())
                    .parseClaimsJws(token.replace("Bearer ", ""))
                    .getBody()
                    .getSubject();

            if (user != null) {
                return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
            }
            return null;
        }
        return null;
    }
}

创建 RESTful API

创建一个 UserController 类来处理用户相关的 API:

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

    @GetMapping
    public List<User> getAllUsers() {
        // 获取所有用户的逻辑
    }

    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        // 根据用户 ID 获取用户的逻辑
    }

    @PostMapping
    public User createUser(@RequestBody User user) {
        // 创建用户的逻辑
    }

    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        // 更新用户的逻辑
    }

    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        // 删除用户的逻辑
    }
}

运行项目

运行项目并访问相应的 API 接口,例如:

curl -X GET http://localhost:8080/api/users

你可以使用 Postman 或其他工具来测试各个接口。

结束语

通过使用 Spring Boot、Spring Security 和 JWT,我们实现了简单且强大的 RESTful API 权限控制。这样的安全机制可以保护我们的应用程序,确保只有经过授权的用户才能访问敏感资源。

希望这篇博客对你了解 Spring Boot + Spring Security + JWT 的实际应用有所帮助,加油!


全部评论: 0

    我有话说: