SpringBoot+SpringSecurity+jwt实现验证

下面我会提供一个基于Spring Boot、Spring Security 和 JSON Web Token(JWT)的认证示例。

一、什么是JWT

JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种简单的、自包含的方式,用于在通过网络进行传输的两个实体之间安全传递信息。它被称为自包含是因为JWT包含了所有身份验证(Authentication)相关的信息,如用户id、用户名等,并且它被设计为在不需调用1次身份验证API的情况下即可用于多个操作(Authorization)。通常情况下,JWT在前端生成、存储并传输给后端进行校验,从而实现身份认证和授权。

JWT 的数据格式是一个由点(.)分割的三部分,这三部分是:

头部(Header):包含JWT的元数据,例如签名算法。
载荷(Payload):包含需要传输的数据。Payload 也被称为声明(claims)。
签名(Signature):由前两个部分的组合生成,从而确保信息在传输过程中没有被篡改。签名算法通常使用HMAC SHA256或RSA SHA256等加密算法。

对于JWT的生成和验证可以通过第三方库实现,例如Java中的jjwt、Python中的pyjwt等。

二、实现步骤

  1. 项目搭建并添加所需依赖

首先,我们在项目中添加 Spring Boot、Spring Security 和 jjwt 依赖:

<!-- SpringBoot -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.4.0</version>
</dependency>

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

<!-- jjwt -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.2</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.2</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.2</version>
    <scope>runtime</scope>
</dependency>
  1. 创建用户实体类和DAO

我们需要有一个 User 实体类来存储用户相关信息,包括用户名、密码等。此外,还需要一个 UserDao 接口来实现用户操作的方法,在示例中,我们采用了基于 MySQL 数据库的数据存储方式。这里就不展开讲解实体类和DAO的实现了。

  1. 对密码进行加密

我们需要在用户注册时就对密码进行加密存储,而不是明文存储。在 Spring Security 中,我们可以通过实现PasswordEncoder接口来完成密码加密的工作。这里我们演示一下BCryptPasswordEncoder的使用方法:

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}
  1. 创建JWT的Token生成工具类

创建一个 JWT 的 token 生成工具类,用于生成和解析 JWT 的 Token。这里采用JJWT库的API实现,上面我们已经在项目中添加了所需依赖。

示例代码如下(请注意,这里的 SECRET_KEY 的值需要根据实际情况设置):

@Component
public class JwtTokenUtil {
    private static final String SECRET_KEY = "12345678";

    public String generateToken(User user) {
        Claims claims = Jwts.claims().setSubject(user.getUsername()); // 将用户名称放进去
        claims.put("password", user.getPassword()); // 放入用户密码

        return Jwts.builder()
                .setClaims(claims) // 放入自定义信息
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24)) // 设置过期时间为1天
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY) // 使用签名算法HS256生成签名
                .compact();
    }

    public Claims parseToken(String token) {
        try {
            return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
        } catch (Exception e) {
            return null;
        }
    }
}
  1. 对用户进行身份验证

在Spring Security中,通常需要实现一个 UserDetailsService 接口以指定如何从数据库中获取用户信息,这样 Spring Security 就可以使用此接口来验证用户身份。

@Service
public class UserServiceImpl implements UserDetailsService {
    @Autowired
    private UserDao userDao;

    // 实现接口方法
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Optional<User> user = userDao.findByUsername(username);
        if (user.isPresent()) {
            return new org.springframework.security.core.userdetails.User(
                    user.get().getUsername(),
                    user.get().getPassword(),
                    Collections.emptyList());
        }
        throw new UsernameNotFoundException("用户名或密码不正确");
    }
}
  1. 创建登录接口及登录成功后生成 Token

处理用户登录的请求,并在登录成功后生成 JWT Token 并返回给客户端。在示例中,我们采用了表单提交的方式进行登录验证。

这里是一个基本的登录控制器的实现:

@RestController
public class LoginController {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserServiceImpl userService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) throws Exception {
        // 构造 Authentication 对象
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword());

        // 通过 AuthenticationManager 进行验证
        Authentication authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication); // 将用户信息保存在 SecurityContext 中

        // 生成 JWT Token
        String token = jwtTokenUtil.generateToken((User)authentication.getPrincipal());
        return ResponseEntity.ok(new LoginResponse(token));
    }
}
  1. 创建需要验证Token的API接口

创建需要进行 Token 验证的 API 接口。

示例代码如下:

@RestController
public class ApiController {
    @GetMapping("/hello")
    public ResponseEntity<?> hello() {
        return ResponseEntity.ok("Hello, 这是被身份验证保护的内容!");
    }
}
  1. 实现一个TokenAuthenticationFilter

自定义一个TokenAuthenticationFilter,用于从请求头中获取 jwt token 并进行校验。如果校验成功,则将已验证的 Authentication 对象放入 Spring Security 的 SecurityContext 中,便于后续的访问。

示例代码如下:

public class TokenAuthenticationFilter extends OncePerRequestFilter {
    private static final String HEADER = "Authorization";
    private static final String PREFIX = "Bearer ";

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserServiceImpl userService;

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

            if (token != null && jwtTokenUtil.parseToken(token) != null) {
                String username = jwtTokenUtil.parseToken(token).getSubject();
                UserDetails userDetails = userService.loadUserByUsername(username);

                if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                    UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
                }
            }
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
        }
    }

    private String getToken(HttpServletRequest request) {
        String authHeader = request.getHeader(HEADER);

        if (authHeader != null && authHeader.startsWith(PREFIX)) {
            return authHeader.replace(PREFIX, "");
        }

        return null;
    }
}
  1. 配置Security,添加TokenAuthenticationFilter

在 Spring Security 的配置文件中添加TokenAuthenticationFilter。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserServiceImpl userService;

    @Autowired
    private TokenAuthenticationFilter tokenAuthenticationFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.POST, "/login").permitAll() // 开放POST的/login接口
                .anyRequest().authenticated() // 其它接口都需要身份验证
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 不用session机制
                .and()
                .addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
                .exceptionHandling()
                .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)); // 认证失败返回 401 状态码
    }


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

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

    // 注册 TokenAuthenticationFilter
    @Bean
    public TokenAuthenticationFilter authenticationTokenFilterBean() {
        return new TokenAuthenticationFilter();
    }
}

三、实际应用

以上就是一个简单的 Spring Boot + Spring Security + JWT 实现身份验证的完整攻略。在实际应用中,可以依据需求进行安全策略的制定,加固授权机制等。

接下来给出一个基于上面实现的Token的简单API调用示例,为了方便,我们使用Postman进行调用。

示例一:调用 /login 接口并返回 Token

访问URL: http://localhost:8080/login

提交数据结构和示例:

{
    "username": "admin",
    "password": "123456"
}

返回Token:

{
    "token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInBhc3N3b3JkIjoiJDJhJDA4JFlPcVV6dHVucDB5NG5qd1JCb0t0U3liWldRSlNTRkxiNG9EM3B5ZFFRUjdvU3drYWQzLlBycyIsImV4cCI6MTYwNzQzNjAzOH0.UgfjTTJFKuErdp0rJ-wVZowv0aT_3HbJ5BPn5xC2r-Y"
}

示例二:调用被保护的 /hello 接口

URL: http://localhost:8080/hello

Headers中添加下面这行:

Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInBhc3N3b3JkIjoiJDJhJDA4JFlPcVV6dHVucDB5NG5qd1JCb0t0U3liWldRSlNTRkxiNG9EM3B5ZFFRUjdvU3drYWQzLlBycyIsImV4cCI6MTYwNzQzNjAzOH0.UgfjTTJFKuErdp0rJ-wVZowv0aT_3HbJ5BPn5xC2r-Y

返回内容:

Hello, 这是被身份验证保护的内容!

好了,至此,我们已经实现了一个简单的 Token 验证示例。希望对你能有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot+SpringSecurity+jwt实现验证 - Python技术站

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

相关文章

  • Java定时清理过期文件的实例代码

    好的。首先,我们需要明确一下清理过期文件的过程,需要完成以下几步: 扫描指定目录下的所有文件; 判断文件的创建时间是否超过指定的过期时间; 如果文件已经过期,就将其删除。 接下来,我们就可以开始编写 Java 定时清理过期文件的实例代码了。 步骤一 首先,我们需要定义一个方法,用于扫描指定目录下的所有文件。代码如下: private static List&…

    Java 2023年5月19日
    00
  • SpringBoot2.x配置HTTPS访问的过程

    下面是“SpringBoot2.x配置HTTPS访问的过程”的完整攻略。 1. 生成证书 首先需要生成一对密钥(证书和私钥),可以使用 keytool 工具来生成。在终端中执行以下命令: keytool -genkeypair -alias mycertalias -keyalg RSA -keysize 2048 -storetype PKCS12 -ke…

    Java 2023年5月19日
    00
  • java导出生成csv文件的方法

    下面我来讲解一下Java导出生成CSV文件的方法。 步骤一:引入CSV依赖 CSV是指Comma Separated Values,即逗号分隔值。在Java中,我们需要引入一个CSV操作的依赖包,这里我们以OpenCSV为例。可以通过以下方式引入依赖: <dependency> <groupId>com.opencsv</gro…

    Java 2023年5月26日
    00
  • java8 时间日期的使用与格式化示例代码详解

    Java8 时间日期的使用与格式化 Java8 初次加入了时间日期处理的新框架–java.time包。新的 API 与 Joda-Time 库有相似的设计理念,但并不是在其基础上构建的。Java8 新的日期和时间库具备了更好的语意化表述,更加明确和易于使用。 基本用法 LocalDate、LocalDateTime、LocalTime 是最常用的日期时间类…

    Java 2023年5月20日
    00
  • springBoot系列常用注解(小结)

    那我会从以下几个方面为您详细讲解springBoot系列常用注解: Spring Boot注解概述 Spring Boot常用注解 Spring Boot常见注解示例解析 1. Spring Boot注解概述 Spring Boot是Spring开发团队为简化Spring开发而设计的一个轻量级框架。在使用Spring Boot中,注解是至关重要,它们可以用来…

    Java 2023年5月15日
    00
  • 原生JS实现不断变化的标签

    实现不断变化的标签通常指的是像轮播图、动态效果等需要不断切换的元素。在原生JS实现这类效果时,可以使用定时器setTimeout或setInterval来实现,通过不断修改元素的属性值,从而达到动态变化的效果。 下面是一个基本的实现步骤: 1. HTML结构 首先,在HTML中需要定义需要变化的元素,比如轮播图的图片。这里以轮播图为例,HTML结构可以参考以…

    Java 2023年6月15日
    00
  • java计算工作时间除去节假日以及双休日

    要计算Java中工作时间(即除去节假日和双休日),一般的做法是使用第三方库或者手动编写代码来计算时间间隔并排除非工作日的时间。下面是两种实现方式的介绍。 使用第三方库 Java中有一些第三方库可以方便地计算时间间隔并排除非工作日。其中一种比较常用的是Joda-Time库。在计算时间间隔时,可以使用Period类,该类可以计算两个日期之间的天数、小时数、分钟数…

    Java 2023年5月20日
    00
  • 初次使用IDEA创建maven项目的教程

    下面是初次使用IDEA创建maven项目的完整攻略。 1. 下载并安装IDEA 首先需要下载和安装IntelliJ IDEA,官网下载地址:https://www.jetbrains.com/idea/download/。选择适配你操作系统的版本下载即可。 2. 创建Maven项目 2.1 打开IntelliJ IDEA,点击“Create New Proj…

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