第24章 Spring Boot RESTful API安全防护:JWT认证实战

在上一篇文章【# Spring Boot新手指南:一文学会RESTful API开发】中,我们深入探讨了如何使用Spring MVC构建RESTful API,并且了解了多种注解的应用,如@RestController、@RequestMapping、@GetMapping等,它们共同帮助我们设计出了既简洁又高效的API接口。然而,在实际开发过程中,除了关注API的功能实现外,维护一份清晰、准确的API文档同样至关重要。这不仅能提升团队内部的协作效率,还能极大地便利外部用户的接入与使用。本文将带你深入探索如何在Spring Boot应用中使用JWT(JSON Web Token)来保障RESTful API的安全性。我们将从为什么API安全性至关重要讲起,继而详细介绍JWT的工作原理及其在Spring Boot项目中的具体实现。通过本教程,你将学会如何为你的API添加强大的身份验证和授权机制,确保数据的安全传输,防止恶意攻击,并提高系统的整体健壮性。无论是初学者还是有一定经验的开发者,都能从中受益,构建出既高效又安全的Web应用。

知识回顾

为何API安全性很重要?

在设计和部署RESTful API时,有如下三个核心缘由能够解释为何安全性应该是一个很重要的考虑因素。安全

1.数据保护

RESTful API是一种向外界传输价值的服务方式。所以,保护经过RESTful方式提供的数据始终应该是属于高优先级。服务器

2.DOS攻击

若是不采起正确的安全措施,(DOS)攻击可使RESTful API进入非功能状态。考虑到不少基础的RESTful API是开放给全部人使用的状况,经过这种相似开源的方式有助于它更好推广给市场,让更多人投入使用,但同时意味着若是有人选择对API执行DOS攻击时,它也可能致使灾难性的结果。restful

3.商业影响

现在有越来多的服务平台,提供着影响衣食住行的各类信息,从飞机航班时刻表到高铁余票查询,甚至只是超市里平常用品,都能给你提供价格、数量、时间等诸多信息,让你足不出户,买到最合心意的商品。在这样的大趋势下,这种利用API数据来获取更多信息,再提供给你的聚合服务平台将会愈来愈多。因而经过RESTful API传输的信息会被频繁调用,而其中的我的信息很容易被泄露。

一、如何保证接口安全?

保证RESTful API的安全性,主要包括三大方面:

a) 对客户端做身份认证 
b) 对敏感的数据做加密,并且防止篡改 
c) 身份认证之后的授权 

恶意提交攻击

1.拦截且修改:客户端发送到服务端接口的请求被第三方拦截,然后修改数据,再提交给客户端执行

例如:

  1. 客户端发送了将username重置为“小明”的请求
  2. 第三方拦截后修改为username重置为”小红”
  3. 然后提交给服务器执行
  4. 服务器认证了用户身份后(cookie或token都是客户端发出的,因此能通过认证)
  5. 那么客户端的username就被修改为“小红”

2.拦截不修改,重复提交攻击

例如:

  1. 客户端发送一个更新user表的请求
  2. 第三方拦截
  3. 然后短时间内复制重复提交
  4. 服务器认证后不断往数据库写数据

任务描述

任务要求

使用IDEA开发工具构建一个项目多模块工程。study-springboot-chapter19学习关于Springboot开发服务接口如果保证访问安全

  1. 基于study-springboot工程,复制study-springboot-chapter19标准项目,坐标groupId(com.cbitedu)、artifactId(study-springboot-chapter19),其他默认
  2. 继承study-springboot工程依赖
  3. 构建Restful API jwt安全解决方案

任务收获

  1. Spring Boot开发Restful API服务,通过JWT保证服务安全
  2. 学会使用JUnit完成单元测试
  3. 掌握web开发原理
  4. 熟悉SpringMVC的基础配置

任务准备

环境要求

  1. JDK1.8+
  2. MySQL8.0.27+
  3. Maven 3.6.1+
  4. IDEA/VSCode

工程目录要求

study-springboot-chapter19

数据准备




DROP TABLE IF EXISTS `jjwt`;
CREATE TABLE `jjwt` (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;




BEGIN;
INSERT INTO `jjwt` (`id`, `username`, `password`) VALUES (1, 'admin', '123456');
COMMIT;

任务实施

JWT可以理解为一个加密的字符串,里面由三部分组成:头部(Header)、负载(Payload)、签名(signature)

由base64加密后的header和payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了JWT字符串

环境搭建

第一步引入第三方依赖:主要是jjwt和knife4j

   <dependency>
            <groupId>com.auth0groupId>
            <artifactId>java-jwtartifactId>
            <version>4.0.0version>
        dependency>       
    <dependency>
            <groupId>com.github.xiaoymingroupId>
            <artifactId>knife4j-spring-boot-starterartifactId>
            <version>3.0.3version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.plugingroupId>
                    <artifactId>spring-plugin-coreartifactId>
                exclusion>
            exclusions>
        dependency>
    <dependency>
        <groupId>org.springframework.plugingroupId>
        <artifactId>spring-plugin-coreartifactId>
        <version>2.0.0.RELEASEversion>
    dependency>

第二步:jwt辅助类:JWTUtils

package com.cbitedu.springboot.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

public class JWTUtils {
    public  static  String SIGN = "ASDFse@#w";
    
    public static String createToken(Map<String,String> payload){
        JWTCreator.Builder builder = JWT.create();
        
        payload.forEach((k,v)->{
            builder.withClaim(k,v);
        });

        HashMap<String, Object> header = new HashMap<>();
        header.put("alg", "HS256");  
        header.put("typ", "JWT");
        builder.withHeader(header);

        Calendar cr = Calendar.getInstance();
        cr.add(Calendar.SECOND,100);    
        System.out.println(cr.getTime());
        builder.withExpiresAt(cr.getTime());                
        return builder.sign(Algorithm.HMAC256(SIGN));     
    }

    
    public static DecodedJWT verifyToken(String token){
        return JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
    }
}

第三步:swagger3和token拦截器

package com.cbitedu.springboot.config;

import io.swagger.annotations.Api;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
@Configuration
@EnableOpenApi
public class Swagger3Config {
    @Bean
    public Docket apiConfig(){
        return new Docket(DocumentationType.OAS_30)
                .apiInfo(apiInfo())
                .select()
                
                
                .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
                
                .paths(PathSelectors.any())
                .build();
    }
    private ApiInfo apiInfo(){
        return new ApiInfoBuilder()
                .title("API安全服务项目")
                .description("项目描述信息")
                .contact(new Contact("creatorblue","http://www.creatorblue.com","zhujianwu@creatorblue.com"))
                .version("1.0")
                .build();
    }
}
package com.cbitedu.springboot.config;

import com.cbitedu.springboot.intercepter.JWTinterepter;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JWTinterepter())
                .addPathPatterns("/user/verity")
                .excludePathPatterns("/user/login");
    }
}
package com.cbitedu.springboot.intercepter;

import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.cbitedu.springboot.util.JWTUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

public class JWTinterepter implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Map map = new HashMap<>();

        
        String token = request.getHeader("token");
        System.out.println("token:"+token);
        try {
            JWTUtils.verifyToken(token);
            return true;
        } catch (TokenExpiredException tokenExpiredException){
            map.put("msg","token过期");
        }catch (AlgorithmMismatchException algorithmMismatchException){
            map.put("msg","token算法不一致");
        }catch (Exception e){
            map.put("msg","token无效");
        }
        map.put("state",false);

        String s = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charest=UTF-8");
        response.getWriter().println(s);
        return false;
    }
}

第四步:控制器:UserController

package com.cbitedu.springboot.controller;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cbitedu.springboot.entity.User;
import com.cbitedu.springboot.service.UserService;
import com.cbitedu.springboot.util.JWTUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import java.util.HashMap;
import java.util.Map;



@RestController
@Slf4j
@Api(tags = {"用户管理"})
public class UserController {
    @Autowired
    private UserService userService;
    @RequestMapping("/")
    public ModelAndView index(User user) {
        return new ModelAndView("login");

    }
    @PostMapping("/user/login")
    @ApiOperation(value = "显示用户数据")
    public Map<String, Object> login(@RequestParam(value="username", required=true) String username,@RequestParam(value="password", required=false) String password) {
        log.info("用户名:" + username);
        log.info("密码:" + password);
        Map<String, Object> map = new HashMap<>();
        try {
            User userDB = userService.getOne(new QueryWrapper<User>().eq("username", username));
            if (userDB != null) {
                map.put("msg", "登录成功");
                map.put("code", "200");
                Map<String, String> payload = new HashMap<>();
                payload.put("name", username);
                String token = JWTUtils.createToken(payload);
                map.put("token", token);
            }
        } catch (Exception ex) {
            map.put("msg", "登录失败");
        }

        return map;
    }

    @GetMapping("/user/verity")
    public Map<String, String> verityToken(String token) {
        Map<String, String> map = new HashMap<>();
        log.info("token===="+token);
        try{
            JWTUtils.verifyToken(token);
            map.put("msg", "验证成功");
            map.put("state", "true");
        }
      catch (Exception exception){
            map.put("msg","验证失败");
            exception.printStackTrace();
      }
        return map;
    }
}

四、测试API文档访问地址:http://localhost:81/doc.html

实验实训

1、认真学习Knife4j:doc.xiaominfo.com/docs/quick-…

2、了解国产API管理平台ApiFox:www.apifox.cn/

阅读全文
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.shuli.cc/?p=20999,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?