经验分享:如何让自己的项目安全性更高

本文分享了在Java项目开发中如何通过防止SQL注入和使用JWT实现登录校验,以及验证码组件的应用,讨论了前后端分离在权限控制中的实践。
摘要由CSDN通过智能技术生成

前言:

不知不觉我已经学习java快四个月了,最近在做一些小项目练手,在做这些项目的过程中,除了通过项目本身基础需求需要照着开发文档或相关规范进行设计的同时,我往往会从中联想到一些更长远的设计目标亦或是更实用的功能设计以便贴合用户的需要。以下我就通过分享一些可能较为常用的组件来让自己的项目功能更加完备:

(本篇)从项目整体安全性角度出发:

一、防止sql注入:

SQL注入是一种常见的网络安全漏洞和攻击方式,它利用应用程序对用户输入数据的处理不当,使得攻击者能够在执行SQL查询时插入恶意的SQL代码。通过成功注入恶意代码,攻击者可以执行未经授权的数据库操作,获取敏感信息、篡改数据甚至完全破坏数据库。

对于防止sql注入的措施有很多,但最为基础常见的就是“避免动态拼接SQL语句”,我们可以使用准备语句(准备语句是一种预编译的SQL语句,其中参数值使用占位符表示。通过使用准备语句,可以将参数值与SQL语句分离,确保输入的数据不会被误解为SQL代码。这样可以有效地防止SQL注入攻击)来避免注入,以下是一个示例:

where后的条件一直成立,用户可进行登录

这个sql语句是使用'?'进行占位的,是预编译的sql,后面参数就会对'?'进行替换,因此不管输入什么字符串都会把整个字符串当作一个参数进行替换传递给对应值,有效防止了sql注入

二:登录校验功能:

在实际开发中,我们会发现有时在未登录的情况下也能直接访问一些需要进行登录的功能,因此我们可以通过一些方法进行登录校验,确认在登录情况下才能做一系列操作:

jwt令牌:

首先要引入依赖

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

 在Web配置类中可以自定义拦截器JwtInterceptor,并设置拦截规则

@Configuration
public class WebConfig implements  WebMvcConfigurer {

    @Resource
    private JwtInterceptor jwtInterceptor;

    // 加自定义拦截器JwtInterceptor,设置拦截规则
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor).addPathPatterns("/**")
                .excludePathPatterns("/")
                .excludePathPatterns("/login")
                .excludePathPatterns("/register")
                .excludePathPatterns("/files/**");
    }
}

通过具体的项目具体设置即可

示例:

/**
 * jwt拦截器
 */
@Component
public class JwtInterceptor implements HandlerInterceptor {

    private static final Logger log = LoggerFactory.getLogger(JwtInterceptor.class);

    @Resource
    private AdminService adminService;
    @Resource
    private UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 1. 从http请求的header中获取token
        String token = request.getHeader(Constants.TOKEN);
        if (ObjectUtil.isEmpty(token)) {
            // 如果没拿到,从参数里再拿一次
            token = request.getParameter(Constants.TOKEN);
        }
        // 2. 开始执行认证
        if (ObjectUtil.isEmpty(token)) {
            throw new CustomException(ResultCodeEnum.TOKEN_INVALID_ERROR);
        }
        Account account = null;
        try {
            // 解析token获取存储的数据
            String userRole = JWT.decode(token).getAudience().get(0);
            String userId = userRole.split("-")[0];
            String role = userRole.split("-")[1];
            // 根据userId查询数据库
            if (RoleEnum.ADMIN.name().equals(role)) {
                account = adminService.selectById(Integer.valueOf(userId));
            } else if (RoleEnum.USER.name().equals(role)) {
                account = userService.selectById(Integer.valueOf(userId));
            }
        } catch (Exception e) {
            throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
        }
        if (ObjectUtil.isNull(account)) {
            throw new CustomException(ResultCodeEnum.USER_NOT_EXIST_ERROR);
        }
        try {
            // 用户密码加签验证 token
            JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(account.getPassword())).build();
            jwtVerifier.verify(token); // 验证token
        } catch (JWTVerificationException e) {
            throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
        }
        return true;
    }
}

三:用户注册登录的验证码组件:

考虑到实际开发的用户账户安全需要,我们可以在前端的登录注册页面引入验证码组件:

<template>
    <div class="s-canvas">
      <canvas id="s-canvas" :width="contentWidth" :height="contentHeight"></canvas>
    </div>
  </template>
  <script>
  export default {
    name: 'Identify',
    props: {
      identifyCode: {
        type: String,
        default: '1234'
      },
      fontSizeMin: {
        type: Number,
        default: 28
      },
      fontSizeMax: {
        type: Number,
        default: 40
      },
      backgroundColorMin: {
        type: Number,
        default: 180
      },
      backgroundColorMax: {
        type: Number,
        default: 240
      },
      colorMin: {
        type: Number,
        default: 50
      },
      colorMax: {
        type: Number,
        default: 160
      },
      lineColorMin: {
        type: Number,
        default: 40
      },
      lineColorMax: {
        type: Number,
        default: 180
      },
      dotColorMin: {
        type: Number,
        default: 0
      },
      dotColorMax: {
        type: Number,
        default: 255
      },
      contentWidth: {
        type: Number,
        default: 112
      },
      contentHeight: {
        type: Number,
        default: 35
      }
    },
    methods: {
      // 生成一个随机数
      randomNum (min, max) {
        return Math.floor(Math.random() * (max - min) + min)
      },
      // 生成一个随机的颜色
      randomColor (min, max) {
        var r = this.randomNum(min, max)
        var g = this.randomNum(min, max)
        var b = this.randomNum(min, max)
        return 'rgb(' + r + ',' + g + ',' + b + ')'
      },
      drawPic () {
        var canvas = document.getElementById('s-canvas')
        var ctx = canvas.getContext('2d')
        ctx.textBaseline = 'bottom'
        // 绘制背景
        ctx.fillStyle = this.randomColor(
            this.backgroundColorMin,
            this.backgroundColorMax
        )
        ctx.fillRect(0, 0, this.contentWidth, this.contentHeight)
        // 绘制文字
        for (let i = 0; i < this.identifyCode.length; i++) {
          this.drawText(ctx, this.identifyCode[i], i)
        }
        this.drawLine(ctx)
        this.drawDot(ctx)
      },
      drawText (ctx, txt, i) {
        ctx.fillStyle = this.randomColor(this.colorMin, this.colorMax)
        ctx.font =
            this.randomNum(this.fontSizeMin, this.fontSizeMax) + 'px SimHei'
        var x = (i + 1) * (this.contentWidth / (this.identifyCode.length + 1))
        var y = this.randomNum(this.fontSizeMax, this.contentHeight - 5)
        var deg = this.randomNum(-30, 30)
        // 修改坐标原点和旋转角度
        ctx.translate(x, y)
        ctx.rotate(deg * Math.PI / 270)
        ctx.fillText(txt, 0, 0)
        // 恢复坐标原点和旋转角度
        ctx.rotate(-deg * Math.PI / 270)
        ctx.translate(-x, -y)
      },
      drawLine (ctx) {
        // 绘制干扰线
        for (let i = 0; i < 2; i++) {
          ctx.strokeStyle = this.randomColor(
              this.lineColorMin,
              this.lineColorMax
          )
          ctx.beginPath()
          ctx.moveTo(
              this.randomNum(0, this.contentWidth),
              this.randomNum(0, this.contentHeight)
          )
          ctx.lineTo(
              this.randomNum(0, this.contentWidth),
              this.randomNum(0, this.contentHeight)
          )
          ctx.stroke()
        }
      },
      drawDot (ctx) {
        // 绘制干扰点
        for (let i = 0; i < 20; i++) {
          ctx.fillStyle = this.randomColor(0, 255)
          ctx.beginPath()
          ctx.arc(
              this.randomNum(0, this.contentWidth),
              this.randomNum(0, this.contentHeight),
              1,
              0,
              2 * Math.PI
          )
          ctx.fill()
        }
      }
    },
    watch: {
      identifyCode () {
        this.drawPic()
      }
    },
    mounted () {
      this.drawPic()
    }
  }
  </script>
  <style scoped>
  .s-canvas {
    height: 38px;
  }
  .s-canvas canvas{
    margin-top: 1px;
    margin-left: 8px;
  }
  </style>

四:前后台能否分离:

在未学习权限管理的情况下,我们可以通过element提供的按钮簇对用户和管理员的身份进行选择

然后加入这个判断逻辑只允许管理员访问后台,普通用户就无法访问

以上就是我认为做项目时在安全性上需要考虑的点,本人还是学生小白,若有纰漏,恳请斧正,不吝赐教!(尚未完全更完)

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值