怎么使用SpringBoot+SpringSecurity+JWT实现系统认证与授权

发布时间:2022-08-09 16:39:07 作者:iii
来源:亿速云 阅读:310

怎么使用SpringBoot+SpringSecurity+JWT实现系统认证与授权

目录

  1. 引言
  2. Spring Boot 简介
  3. Spring Security 简介
  4. JWT 简介
  5. 项目搭建
  6. Spring Security 配置
  7. JWT 集成
  8. 用户认证
  9. 用户授权
  10. 测试与验证
  11. 总结

引言

在现代的Web应用中,系统的安全性和用户的权限管理是非常重要的。Spring Boot 快速开发框架,结合 Spring Security 和 JWT(JSON Web Token)可以很好地实现系统的认证与授权。本文将详细介绍如何使用 Spring Boot、Spring Security 和 JWT 来实现系统的认证与授权。

Spring Boot 简介

Spring Boot 是 Spring 框架的一个子项目,旨在简化 Spring 应用的初始搭建和开发过程。它通过自动配置和约定优于配置的原则,使得开发者可以快速启动和运行 Spring 应用。

主要特点

Spring Security 简介

Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架。它是 Spring 生态系统中的一个重要组成部分,用于保护基于 Spring 的应用程序。

主要功能

JWT 简介

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间安全地传输信息作为 JSON 对象。JWT 通常用于身份验证和信息交换。

JWT 结构

JWT 由三部分组成:

  1. Header:包含令牌的类型和使用的加密算法。
  2. Payload:包含声明(claims),声明是关于实体(通常是用户)和其他数据的声明。
  3. Signature:用于验证消息在传输过程中没有被篡改。

JWT 的优点

项目搭建

创建 Spring Boot 项目

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

  1. 打开 Spring Initializr
  2. 选择项目类型为 Maven Project。
  3. 选择语言为 Java。
  4. 选择 Spring Boot 版本(建议选择最新稳定版)。
  5. 填写项目元数据(Group、Artifact、Name 等)。
  6. 添加依赖:Spring Web、Spring Security、Spring Data JPA、H2 Database(用于测试)、Lombok(可选)。
  7. 点击 Generate 按钮下载项目。

项目结构

项目结构如下:

src
├── main
│   ├── java
│   │   └── com
│   │       └── example
│   │           └── demo
│   │               ├── config
│   │               ├── controller
│   │               ├── model
│   │               ├── repository
│   │               ├── security
│   │               └── service
│   └── resources
│       ├── application.properties
│       └── static
└── test
    └── java
        └── com
            └── example
                └── demo

配置 application.properties

src/main/resources/application.properties 文件中添加以下配置:

# Server port
server.port=8080

# H2 Database configuration
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.h2.console.enabled=true

# JWT configuration
jwt.secret=mySecretKey
jwt.expiration=86400000

Spring Security 配置

创建 SecurityConfig 类

src/main/java/com/example/demo/config 目录下创建 SecurityConfig 类,用于配置 Spring Security。

package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/api/auth/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

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

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

配置用户认证

SecurityConfig 类中,我们还需要配置用户认证。可以通过内存中的用户或数据库中的用户来进行认证。

内存中的用户认证

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
        .withUser("user").password(passwordEncoder().encode("password")).roles("USER")
        .and()
        .withUser("admin").password(passwordEncoder().encode("admin")).roles("ADMIN");
}

数据库中的用户认证

如果使用数据库中的用户进行认证,需要创建 UserDetailsService 的实现类。

package com.example.demo.security;

import com.example.demo.repository.UserRepository;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class CustomUserDetailsService implements UserDetailsService {

    private final UserRepository userRepository;

    public CustomUserDetailsService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found with username: " + username));
    }
}

然后在 SecurityConfig 类中配置 CustomUserDetailsService

@Autowired
private CustomUserDetailsService userDetailsService;

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

JWT 集成

创建 JWT 工具类

src/main/java/com/example/demo/security 目录下创建 JwtTokenProvider 类,用于生成和验证 JWT。

package com.example.demo.security;

import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class JwtTokenProvider {

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

    @Value("${jwt.expiration}")
    private int jwtExpirationInMs;

    public String generateToken(Authentication authentication) {
        UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();

        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + jwtExpirationInMs);

        return Jwts.builder()
                .setSubject(Long.toString(userPrincipal.getId()))
                .setIssuedAt(new Date())
                .setExpiration(expiryDate)
                .signWith(SignatureAlgorithm.HS512, jwtSecret)
                .compact();
    }

    public Long getUserIdFromJWT(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(jwtSecret)
                .parseClaimsJws(token)
                .getBody();

        return Long.parseLong(claims.getSubject());
    }

    public boolean validateToken(String authToken) {
        try {
            Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
            return true;
        } catch (SignatureException ex) {
            // Invalid JWT signature
        } catch (MalformedJwtException ex) {
            // Invalid JWT token
        } catch (ExpiredJwtException ex) {
            // Expired JWT token
        } catch (UnsupportedJwtException ex) {
            // Unsupported JWT token
        } catch (IllegalArgumentException ex) {
            // JWT claims string is empty
        }
        return false;
    }
}

创建 JWT 过滤器

src/main/java/com/example/demo/security 目录下创建 JwtAuthenticationFilter 类,用于在请求中提取 JWT 并进行验证。

package com.example.demo.security;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

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

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtTokenProvider tokenProvider;
    private final CustomUserDetailsService customUserDetailsService;

    public JwtAuthenticationFilter(JwtTokenProvider tokenProvider, CustomUserDetailsService customUserDetailsService) {
        this.tokenProvider = tokenProvider;
        this.customUserDetailsService = customUserDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        try {
            String jwt = getJwtFromRequest(request);

            if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
                Long userId = tokenProvider.getUserIdFromJWT(jwt);

                UserDetails userDetails = customUserDetailsService.loadUserById(userId);
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        } catch (Exception ex) {
            logger.error("Could not set user authentication in security context", ex);
        }

        filterChain.doFilter(request, response);
    }

    private String getJwtFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}

配置 JWT 过滤器

SecurityConfig 类中配置 JwtAuthenticationFilter

@Autowired
private JwtTokenProvider tokenProvider;

@Autowired
private CustomUserDetailsService customUserDetailsService;

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
        .authorizeRequests()
        .antMatchers("/api/auth/**").permitAll()
        .anyRequest().authenticated()
        .and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    http.addFilterBefore(new JwtAuthenticationFilter(tokenProvider, customUserDetailsService), UsernamePasswordAuthenticationFilter.class);
}

用户认证

创建用户模型

src/main/java/com/example/demo/model 目录下创建 User 类,用于表示用户实体。

package com.example.demo.model;

import javax.persistence.*;
import java.util.Collection;

@Entity
public class User {

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

    private String username;
    private String password;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(
            name = "users_roles",
            joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
            inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id")
    )
    private Collection<Role> roles;

    // Getters and Setters
}

创建角色模型

src/main/java/com/example/demo/model 目录下创建 Role 类,用于表示角色实体。

package com.example.demo.model;

import javax.persistence.*;
import java.util.Collection;

@Entity
public class Role {

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

    private String name;

    @ManyToMany(mappedBy = "roles")
    private Collection<User> users;

    // Getters and Setters
}

创建用户仓库

src/main/java/com/example/demo/repository 目录下创建 UserRepository 接口,用于操作用户数据。

package com.example.demo.repository;

import com.example.demo.model.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

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

创建用户服务

src/main/java/com/example/demo/service 目录下创建 UserService 类,用于处理用户相关的业务逻辑。

package com.example.demo.service;

import com.example.demo.model.User;
import com.example.demo.repository.UserRepository;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;

    public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
    }

    public User registerUser(User user) {
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        return userRepository.save(user);
    }
}

创建认证控制器

src/main/java/com/example/demo/controller 目录下创建 AuthController 类,用于处理用户认证相关的请求。

package com.example.demo.controller;

import com.example.demo.model.User;
import com.example.demo.payload.JwtAuthenticationResponse;
import com.example.demo.payload.LoginRequest;
import com.example.demo.payload.SignUpRequest;
import com.example.demo.security.JwtTokenProvider;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtTokenProvider tokenProvider;

    @Autowired
    private UserService userService;

    @PostMapping("/signin")
    public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest) {
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        loginRequest.getUsername(),
                        loginRequest.getPassword()
                )
        );

        SecurityContextHolder.getContext().setAuthentication(authentication);

        String jwt = tokenProvider.generateToken(authentication);
        return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
    }

    @PostMapping("/signup")
    public ResponseEntity<?> registerUser(@RequestBody SignUpRequest signUpRequest) {
        User user = new User();
        user.setUsername(signUpRequest.getUsername());
        user.setPassword(signUpRequest.getPassword());

        User result = userService.registerUser(user);

        return ResponseEntity.ok(result);
    }
}

创建认证请求和响应类

src/main/java/com/example/demo/payload 目录下创建 LoginRequestSignUpRequestJwtAuthenticationResponse 类,用于表示认证请求和响应。

package com.example.demo.payload;

public class LoginRequest {
    private String username;
    private String password;

    // Getters and Setters
}

package com.example.demo.payload;

public class SignUpRequest {
    private String username;
    private String password;

    // Getters and Setters
}

package com.example.demo.payload;

public class JwtAuthenticationResponse {
    private String accessToken;

    public JwtAuthenticationResponse(String accessToken) {
        this.accessToken = accessToken;
    }

    // Getter
}

用户授权

创建角色仓库

src/main/java/com/example/demo/repository 目录下创建 RoleRepository 接口,用于操作角色数据。

package com.example.demo.repository;

import com.example.demo.model.Role;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface RoleRepository extends JpaRepository<Role, Long> {
    Optional<Role> findByName(String name);
}

创建角色服务

src/main/java/com/example/demo/service 目录下创建 RoleService 类,用于处理角色相关的业务逻辑。

”`java package com.example.demo.service;

import com.example.demo.model.Role; import com.example.demo.repository.RoleRepository; import org.springframework.stereotype.Service;

@Service public class RoleService {

private final RoleRepository roleRepository;

public RoleService(RoleRepository roleRepository) {
    this
推荐阅读:
  1. 如何实现SpringSecurity的认证和授权
  2. 通过session会话实现认证和授权

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

springboot springsecurity jwt

上一篇:Tomcat怎么安装使用及部署Web项目

下一篇:Golang基于JWT与Casbin身份验证授权怎么实现

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》