目录

使用场景:

分析需求实现:

核心思路

1.URL加密:

2.无状态设计:

3.加密工具包:

1.在pom.xml中添加加密工具包的依赖。

2.创建加密工具类

3. 创建URL加密工具类

4. 创建拦截器

5. 注册拦截器

6. 生成加密URL(管理员操作)

7. 接口定义

8. 对加密功能进行演示

1.生成加密接口给用户(模拟管理员操作)

2.获取获取明细账加密url(模拟管理员操作)

3.访问加密访问地址(模拟用户操作)

4.查看控制台输出:

5.访问没有授权的接口(模拟用户操作)

6.测试接口时效性(模拟用户操作)

9.总结


使用场景:

在用户不需要登录的情况下,有两个接口需要提供给外部访问,又需要保证接口有一定的安全性。这时就需要对暴露在接口外面的接口进行加密处理,不能让人知道了url就能拿取数据了吧。所以这时候就要想办法就行一些加密的安全操作。

分析需求实现:

具体思路是这样的,用户需要获取数据的时候,由管理员提供url给用户使用。但是url路径总不能频繁的改变吧,这样可操作性不大。不能频繁的变更接口的url,但是可以给接口规定一段时间的使用时效和对其进行加密,这样既保证了用户的正常使用,又在一定程度上保证了数据的安全。

一言以蔽之,

使用加密工具包,提供密码(字符串 + 时间戳)进行加密,截取提供的内容。

相差10分钟允许访问,携带有效的token允许访问。

没有token不许访问,超时不予访问。

核心思路

1.URL加密:

将需要保护的接口URL进行加密,加密内容包含时间戳和允许访问的有效期(如10分钟)。用户访问时,服务端解密URL中的参数,验证时间戳是否在有效期内。

2.无状态设计:

由于用户不需要登录,接口的加密验证需要是无状态的,即每次访问都需要携带加密参数。

3.加密工具包:

使用对称加密算法(如AES)对数据进行加密和解密。

具体实现如下:

1.在pom.xml中添加加密工具包的依赖。

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.70</version>
</dependency>

2.创建加密工具类

编写一个工具类,用于加密和解密数据。

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

// 用于加密和解密数据。
public class EncryptionUtil {

    private static final String ALGORITHM = "AES";
    private static final String SECRET_KEY = "cherry-and-apple"; // 密钥

    // 加密方法
    public static String encrypt(String data) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(), ALGORITHM);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        byte[] encryptedBytes = cipher.doFinal(data.getBytes());
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }

    // 解密方法
    public static String decrypt(String encryptedData) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(), ALGORITHM);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
        return new String(decryptedBytes);
    }
}

3. 创建URL加密工具类

编写一个工具类,用于生成加密URL和解密URL参数。

//用于生成加密URL和解密URL参数。
public class UrlEncryptionUtil {

    // 生成加密URL
    public static String generateEncryptedUrl(String baseUrl, long timestamp) throws Exception {
        String data = "timestamp=" + timestamp; // 将时间戳作为参数
        String encryptedData = EncryptionUtil.encrypt(data); // 加密
        return baseUrl + "?token=" + encryptedData; // 将加密数据作为URL参数
    }

    // 验证URL参数
    public static boolean validateUrlToken(String token) throws Exception {
        String decryptedData = EncryptionUtil.decrypt(token); // 解密
        String[] parts = decryptedData.split("="); // 解析参数
        if (parts.length != 2 || !parts[0].equals("timestamp")) {
            return false; // 参数格式不正确
        }
        long timestamp = Long.parseLong(parts[1]);
        long currentTime = System.currentTimeMillis();
        return Math.abs(currentTime - timestamp) <= 10 * 60 * 1000; // 验证时间戳是否在10分钟内
    }
}

4. 创建拦截器

使用Spring Boot的拦截器对请求进行拦截,验证URL中的加密参数。

// 对接口包含 “encrypt” 的uri进行拦截

@Slf4j
@Component
// 创建加密拦截器
public class EncryptionInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        log.info("Encryption Interceptor 拦截器执行!\nURI为:{}, \n请求URL为:{}",request.getRequestURI(), request.getRequestURL());
        String token = request.getParameter("token"); // 从URL中获取加密参数

        if (token == null) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.getWriter().write("Missing token parameter");
            return false;
        }

        // 验证加密参数
        try {
            if (!UrlEncryptionUtil.validateUrlToken(token)) { // 验证是否在10分钟以内,超时不予访问!
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                response.getWriter().write("Invalid or expired token");
                return false;
            }
        } catch (Exception e) {
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            response.getWriter().write("Decryption failed");
            return false;
        }

        return true;

//        return HandlerInterceptor.super.preHandle(request, response, handler);
    }
}

5. 注册拦截器

将拦截器注册到Spring Boot中。

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;


@Configuration
// 注册加密拦截器
public class InterceptorConfig implements WebMvcConfigurer {

    @Resource
    private EncryptionInterceptor encryptionInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        // 拦截uri中包含“encrypt”的请求
        registry.addInterceptor(encryptionInterceptor).addPathPatterns("/**/encrypt/**");

    }
}

6. 生成加密URL(管理员操作)

管理员在服务端生成加密URL,提供给用户访问。

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;


/**
 * 生成有效的url给用户 ,供用户在有效时限内使用;
 */

// 获取明细账:http://localhost:8088/dyhs/generate/url/mxz

// 获取余额表:http://localhost:8088/dyhs/generate/url/yeb


@RequestMapping("/generate/url")
@RestController
@Slf4j
public class UrlAccessController {

    private final String protocol = "http://"; // 使用协议

    String hostAddress = getLocalIp(); // 本机IP

    @Value("${server.port}")
    private int port; // 项目端口号

    @Value("${server.servlet.context-path}")
    private String contextPath; // 项目虚拟路径

    // 明细账URI
    private final String mxzAccessUri = "/encrypt/mxz";

    // 余额表URI
    private final String yebAccessUri = "/encrypt/yeb";




    @GetMapping("/mxz")
    @CrossOrigin
    @SneakyThrows
    public String mxz(HttpServletRequest request){
        // 获取baseUrl
        String baseUrl = getBasUrl(mxzAccessUri); // 明细账

        long timestamp = System.currentTimeMillis(); // 当前时间戳
        String encryptedUrl = UrlEncryptionUtil.generateEncryptedUrl(baseUrl, timestamp); // 生成加密URL
        log.info("Encrypted URL: " + encryptedUrl);
        // 返回加密 URL
        return "明细账加密访问地址:</br>"+encryptedUrl+
                "</br></br>有效时间10分钟!";
    }


    @GetMapping("/yeb")
    @CrossOrigin
    @SneakyThrows
    public String yeb(HttpServletRequest request){

        // 获取baseUrl
        String baseUrl = getBasUrl(yebAccessUri); // 余额表

        long timestamp = System.currentTimeMillis(); // 当前时间戳
        String encryptedUrl = UrlEncryptionUtil.generateEncryptedUrl(baseUrl, timestamp); // 生成加密URL
        log.info("Encrypted URL: " + encryptedUrl);
        // 返回加密 URL
        return "余额表加密访问地址:<br/>"+encryptedUrl+
                "<br/><br/>有效时间10分钟!";
    }

    private String getBasUrl( String uriType){
        log.info("本机IP:{}", hostAddress);
        log.info("项目端口号:{}",port);
        log.info("虚拟路径:{}", contextPath);
        String baseUrl = protocol+hostAddress+":"+port+contextPath+uriType;
        log.info("baseUrl: {}", baseUrl);
        return baseUrl;
    }


    @SneakyThrows
    public String getLocalIp(){
        return InetAddress.getLocalHost().getHostAddress();
    }

    @SneakyThrows
    public static void main(String[] args) {
        String hostAddress = InetAddress.getLocalHost().getHostAddress();
        log.info("本机IP:{}", hostAddress);
    }



}

代码片段6

7. 接口定义

在Spring Boot中定义需要加密保护的接口。(具体数据查询细节结合自己的业务实现!)

import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;


/**
 * 明细账
 * 余额表
 * 提供接口查询
 */

@RestController
@RequestMapping("/encrypt") // 接口加密
public class OfferServiceController {

    @Resource
    private DetailService detailService;

    @Resource
    private SumAnaService sumAnaService;

    @GetMapping("/mxz")
    @CrossOrigin
    // 全查 明细账
    public List<MXZ> queryMxzAll(){
        return detailService.queryMxzAll();
    }

    // 全查 余额表 行政、食堂、工会、专项
    @GetMapping("/yeb")
    @CrossOrigin
    public List<Yeb> yeb(){
        return sumAnaService.assemblyYeb();
    }



}

8. 对加密功能进行演示

管理员使用生成加密URL的接口(代码片段6),获取加密的url给用户。

当然这两个接口只有管理员知道。

1.生成加密接口给用户(模拟管理员操作)

获取明细账加密接口:http://localhost:8088/dyhs/generate/url/mxz

获取余额表加密接口:http://localhost:8088/dyhs/generate/url/yeb

2.获取获取明细账加密url(模拟管理员操作)

浏览器输入http://localhost:8088/dyhs/generate/url/mxz得到结果。

3.访问加密访问地址(模拟用户操作)

4.查看控制台输出:

5.访问没有授权的接口(模拟用户操作)

测试访问没有token携带的接口。

6.测试接口时效性(模拟用户操作)

接口具有失效性,超时了不能访问。

获取余额表的加密url和上面获取明细账的加密url操作一样。

9.总结

至此我们实现了对暴露在外给用户使用的接口进行了简单的加密防护,

保证和提供了一定的安全性!

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐