在SpringBoot中使用JWT的实现方法

yizhihongxing

下面我将为您讲解在SpringBoot中使用JWT的实现方法的完整攻略。

1. 什么是JWT

JWT全称是Json Web Token,它是一种基于 JSON 的开放标准(RFC 7519) ,用于在不同的系统之间传递信息,并且保证信息不会被篡改。在进行用户认证、鉴权等领域,JWT被广泛应用。

JWT由三部分组成:

  • Header 头部
  • Payload 载荷(有效的信息)
  • Signature 签名

通过Base64加密,三个部分将被组合在一起,成为一个JWT字符串。

2. 在SpringBoot中使用JWT的步骤

2.1 添加依赖

Spring Security JWT需要依赖JJWT,在pom.xml中添加依赖

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

2.2 配置JWT工具类

在项目中,需要封装一个JWT工具类,来处理JWT的生成、解析等操作,代码示例:

@Component
public class JwtUtil {
    @Value("${jwt.secret}")
    private String secret;

    /**
     * 生成jwt字符串
     * @param userId
     * @param username
     * @return
     */
    public String generateToken(Integer userId, String username) {
        Date nowDate = new Date();
        //过期时间
        Date expireDate = new Date(nowDate.getTime() + 1000 * 60 * 60 * 24 * 7);
        return Jwts.builder()
                .setHeaderParam("typ", "JWT")
                .setSubject(userId.toString())
                .setIssuedAt(nowDate)
                .setExpiration(expireDate)
                .claim("username", username)
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
    }

    /**
     * 获取jwt中的用户信息
     * @param token
     * @return
     */
    public Claims getClaimByToken(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        }catch (Exception e){
            return null;
        }
    }

    /**
     * 判断jwt是否过期失效
     * @param expiration
     * @return true:过期失效
     */
    public boolean isTokenExpired(Date expiration) {
        return expiration.before(new Date());
    }
}

在生成和解析Token时,需要用到指定的加密密钥,这里使用@Value("${jwt.secret}")注入配置文件中配置的密钥。

2.3 自定义UserDetails实现

Spring Security在用户认证中需要知道哪些用户是合法的,哪些用户具有哪些角色和权限。在Spring中使用UserDetailsService接口,通过在该接口的实现类中返回一个 UserDetails 对象来完成用户认证。在这里我们自定义了MyUserDetails类,来实现这个接口,并重写其方法。示例代码:

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    private final UserService userService;

    public UserDetailsServiceImpl(UserService userService) {
        this.userService = userService;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.getUserByUsername(username);
        if (Objects.isNull(user)) {
            throw new UsernameNotFoundException("用户不存在");
        }
        return new MyUserDetails(user);
    }
}

2.4 自定义SecurityConfig配置类

最后一步,我们需要设置WebSecurityConfigurerAdapter,该类是Spring Security提供给开发人员的另一个配置接口。该配置类提供一系列方法来配置Spring Security的安全行为。该类需要自定义编写,并配置相关登录、登出等内容。我们需要继承该类并进行配置。配置过程中,为了保护特定的路径(例如接口),我们将采用 TokenAuthenticationFilter 配置的方式。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    private final UserDetailsServiceImpl userDetailsService;
    private final JwtTokenFilter jwtTokenFilter;

    public SecurityConfig(JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint, UserDetailsServiceImpl userDetailsService, JwtTokenFilter jwtTokenFilter) {
        this.jwtAuthenticationEntryPoint = jwtAuthenticationEntryPoint;
        this.userDetailsService = userDetailsService;
        this.jwtTokenFilter = jwtTokenFilter;
    }

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                // 对于登录、注册允许匿名访问
                .antMatchers("/auth/**").permitAll()
                .antMatchers(HttpMethod.GET, "/user/**").hasAuthority(Role.USER.getRoleName())
                .antMatchers(HttpMethod.POST, "/book/**").hasAuthority(Role.ADMIN.getRoleName())
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated()
                .and()
                .cors()
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        //添加JWT filter
        http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
        // 添加自定义未授权和未登录结果返回
        http.exceptionHandling()
                .accessDeniedHandler(jwtAuthenticationEntryPoint)
                .authenticationEntryPoint(jwtAuthenticationEntryPoint);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        //ignoring Swagger UI resource
        web.ignoring().antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources/**",
                "/configuration/security", "/swagger-ui.html", "/webjars/**", "/favicon.ico", "/index.html");
    }
}

上面的代码显示,利用 HttpSecurity 配置器,我们禁用了 CSRF,但允许匿名访问 /auth ,通过请求方法和授权等方式来限制接口的访问。添加了JWT filter并配置了自定义未授权和未登录的结果返回。

至此,我们完成了JWT的基本配置,并且可以实现用户的认证和用户鉴权。

3. JWT的使用示例

3.1 生成Token

以下代码片段为我们展示如何生成JWT,生成过程需要用到上面讲到的JwtUtil工具类。

 @RequestMapping(value = "/login", method = RequestMethod.POST)
    public CommonResult login(@RequestParam(name = "username") String username,
                              @RequestParam(name = "password") String password){

        User user = userService.getUserByUsername(username);
        if(Objects.isNull(user)){
            return CommonResult.failed("用户不存在");
        }
        if(!passwordEncoder.matches(password,user.getPassword())){
            return CommonResult.failed("密码不正确");
        }

        //登录成功,生成token
        String token = jwtUtil.generateToken(user.getId(), user.getUsername());
        Map<String,String> result = new HashMap<>();
        result.put("token",token);
        result.put("tokenHead",tokenProperties.getTokenHead());
        return CommonResult.success(result);
    }

3.2 接口鉴权

通过上面的配置,我们可以为指定的接口添加鉴权,示例代码如下:

@RestController
@RequestMapping("/book")
@RequiredArgsConstructor
@Api(tags = "图书管理接口")
public class BookController {

    private final BookService bookService;

    @GetMapping("/{id}")
    @ApiOperation("获取图书详情")
    @ApiImplicitParam(name = "id", value = "图书ID", required = true, dataType = "int", paramType = "path")
    @PreAuthorize("hasAuthority('USER')")
    public CommonResult<Book> getBookById(@PathVariable int id){
        return bookService.getBookById(id);
    }

    @PostMapping
    @ApiOperation("新增图书")
    @ApiImplicitParam(name = "book", value = "图书信息", required = true, dataType = "Book")
    @PreAuthorize("hasAuthority('ADMIN')")
    public CommonResult addBook(@RequestBody @Valid Book book){
        return bookService.addBook(book);
    }
}

其中,@PreAuthorize("hasAuthority('USER')") 表示只有拥有USER角色的用户才可以调用该接口。

总结

以上就是在SpringBoot中使用JWT的实现方法的完整攻略,希望对您有所帮助。JWT作为一种常见的用户认证、鉴权,确保我们的服务不易被攻击,可以为我们的项目安全加上一道保护墙,同时也提高了开发效率、降低了维护成本。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:在SpringBoot中使用JWT的实现方法 - Python技术站

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

相关文章

  • jsp搜索引擎

    JSP(Java Server Pages)搜索引擎需要基于Java编程语言进行开发,可以使用已有的开源框架、工具库进行快速开发。 以下是JSP搜索引擎的完整攻略: 步骤一:创建Web应用程序 使用任意一种Java Web框架创建一个全新的Web应用程序。(注意:在接下来的步骤中,以SpringMVC框架为例进行讲解) 步骤二:集成Lucene搜索引擎 Lu…

    Java 2023年6月15日
    00
  • centos7下搭建ZooKeeper3.4中间件常用命令小结

    下面是详细讲解“centos7下搭建ZooKeeper3.4中间件常用命令小结”的完整攻略。 一、ZooKeeper介绍 ZooKeeper是一个分布式协调服务,可以用于分布式应用的协调管理。ZooKeeper提供了高可用性和高性能的数据管理和协调功能,这些功能包括配置管理、命名服务、分布式同步、群组服务等。 二、ZooKeeper安装 以下是在CentOS…

    Java 2023年5月20日
    00
  • 时间处理函数工具分享(时间戳计算)

    下面是“时间处理函数工具分享(时间戳计算)”的完整攻略。 时间戳的概念 时间戳(Timestamp)是指格林威治时间1970年01月01日00时00分01秒(北京时间1970年01月01日08时00分01秒)起至现在的总秒数。时间戳是一种以简洁、统一的方式表示时间的方式,通常被用于记录事件发生的时间或进行时间计算。 Javascript中的时间处理 获取当前…

    Java 2023年5月20日
    00
  • Spring Boot中使用Spring-data-jpa实现数据库增删查改

    下面是关于“Spring Boot中使用Spring-data-jpa实现数据库增删查改”的完整攻略,包括以下内容: 前置条件 引入依赖 创建实体类 创建Repository接口 使用Repository接口实现数据库的增删查改 示例1:新增数据 示例2:查询数据 1. 前置条件 在使用Spring-data-jpa实现数据库操作之前,需要保证本地环境已经安…

    Java 2023年5月20日
    00
  • Java如何判断字符串中是否包含某个字符

    如果需要在Java中判断一个字符串是否包含某个字符,可以使用String类的contains()方法或indexOf()方法。 方法1:contains()方法 contains()方法用于判断一个字符串中是否包含另一个字符串。它返回一个布尔值,表示待判断的字符串是否包含指定的字符或字符串。 下面是一个例子: String str = "hello…

    Java 2023年5月27日
    00
  • Java String字符串和Unicode字符相互转换代码

    下面是Java String字符串和Unicode字符相互转换代码的完整攻略: Unicode字符和Java String字符串的相互转换 在Java编程中,我们有时需要将Unicode字符和Java String字符串相互转换。Unicode字符是一个标准,它规定了所有字符及其对应的码点。而Java String字符串是由Unicode字符序列组成的。 U…

    Java 2023年5月20日
    00
  • Java基础泛型详情

    Java基础泛型详情 什么是泛型 泛型是Java的一种特性,可以让用户在编写代码时将数据类型作为参数进行传递。通过泛型,Java可以实现更加安全、灵活和可读性强的代码。泛型的本质是参数化类型,也就是说,使用时可以在代码中传递各种类型的数据,这样可以避免一些常见的类型错误。 泛型的语法 定义泛型类可以使用以下语法: class 类名<泛型参数1, 泛型参…

    Java 2023年5月26日
    00
  • JavaIO BufferedReader和BufferedWriter使用及说明

    JavaIO BufferedReader和BufferedWriter使用及说明 在Java中,读写文件是非常频繁的操作。BufferedReader和BufferedWriter是常用的文件读写工具类。本文将详细介绍这两个工具类的使用方法及说明。 BufferedReader BufferedReader是一个用来读取字符流的缓冲区。它以一个字符输入流作…

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