功能性的安全性保障:实现强制登录和密码加密功能

前言

在软件开发过程中,确保系统的安全性是至关重要的一环。它不仅关乎保护用户数据的完整性和隐私性,也是维护系统稳定运行的基石。我认为,从宏观角度审视,软件开发的安全性保障主要可分为两大类:功能性的安全性保障和系统性的安全性校验。

功能性的安全性保障专注于应用程序层面,它着眼于那些直接影响用户数据和交互过程安全的特性。这些特性是构建用户信任和保障数据安全的关键。

系统性的安全性校验则放眼于更为广阔的视角,它涵盖了整个系统架构和网络层面,确保从服务器到网络的每一环节都具备足够的防御能力,以抵御各种潜在的攻击。

在本文中,我们将过对这些概念的细致解读、实施策略的全面分析,以及实际代码实现的展示,深入探讨功能性的安全性保障中的两个核心议题:身份验证和密码加密。

1. 单点登录(SSO):一次验证,多个访问

在软件开发中,身份验证是确保数据安全性的基础措施。通过强制用户登录,我们可以确保只有授权用户能够访问敏感数据。这一机制构成了功能性安全保障中的第一道防线,其重要性不容忽视。

单点登录允许用户使用单一的认证过程访问多个系统或应用程序。SSO简化了用户体验,同时通过集中管理身份验证过程,提高了安全性和效率。然而,SSO系统的设计和实现需要格外小心,以防止单点故障或成为攻击的集中目标。

2. 双因素认证(2FA):两类验证,提高安全

双因素认证是多因素认证的一种形式,通常结合了密码和一种其他类型的验证方式,如短信验证码、电子邮件链接或认证器应用生成的代码。2FA为账户安全提供了额外的保护层,即使密码被泄露,攻击者也难以获得账户的完全访问权。

3. 自适应认证:动态认证,调整策略

自适应认证是一种动态的身份验证方法,根据用户的行为、位置、设备和其他上下文信息来调整认证要求的严格程度。例如,如果检测到用户从一个新的地理位置或设备登录,系统可能会要求更严格的认证过程。

4. 代码实现:强制用户登录,确保只有授权用户才能访问敏感数据

4.1 前端代码实现

  1. 首先模拟前端登录操作,定义一个登录页面,提供用户名和密码输入框,以及登录和注册按钮。通过点击“登录”按钮,发送 HTTP POST 请求到后端进行用户验证。登录成功后导航到主页,点击“注册”按钮导航到注册页面。如果登录失败,显示相应的错误信息。
<template>
  <div class="login">
    <h1>Login</h1>
    <el-form ref="form" :model="form" label-width="120px">
      <el-form-item label="Username">
        <el-input v-model="form.username" name="username" placeholder="请输入用户名信息"/>
      </el-form-item>

      <el-form-item label="Password">
        <el-input type="password" v-model="form.password" name="password" placeholder="请输入密码信息"/>
      </el-form-item>
      <el-form-item :inline="true">
        <el-button type="primary" @click="login">登录</el-button>
        <el-button type="primary" @click="goToRegister">注册</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>
<script>
import axios from 'axios';
export default {
  name: 'Login',
  data() {
    return {
      form: {
        username: '',
        password: ''
      }
    };
  },
  methods: {
    goToRegister() {
      this.$router.push({name: 'Register'});
    },
    async login() {
      try {
        const params = new URLSearchParams();
        params.append('username', this.form.username);
        params.append('password', this.form.password);

        const response = await axios.post('http://127.0.0.1:8081/user/login', params);
        console.log("login response:", response);
        if (response.data.code === 200) {
          this.$message.success('登录成功');
          this.$router.push('/');
        } else {
          if (response.data.extension && response.data.extension.error) {
            this.$message.error('登录失败: ' + response.data.extension.error);
            console.log(response.data.extension.error);
          } else {
            this.$message.error('登录失败');
            console.log('未知错误');
          }
        }
      } catch (error) {
        console.log(error);
        this.$message.error('登录失败');
      }
    }
  }
};
</script>

为了提供一个完整的用户体验,我们不仅实现了登录功能,还引入了注册功能。注册页面的设计和实现与登录页面保持一致,确保用户在创建账户时也能享受到顺畅的操作流程。

<template>
  <div class="register">
    <h1>注册界面</h1>
    <el-form ref="form" :model="form" label-width="120px">
      <el-form-item label="Username">
        <el-input v-model="form.username" name="username" placeholder="请输入用户名信息"/>
      </el-form-item>

      <el-form-item label="Password">
        <el-input type="password" v-model="form.password" name="password" placeholder="请输入密码信息"/>
      </el-form-item>
      <el-form-item :inline="true">
        <el-button type="primary" @click="register">注册</el-button>
        <el-button type="primary" @click="goToLogin">登录</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
import {ref} from 'vue';
import axios from 'axios';
import {useRouter} from 'vue-router';
import {ElMessage} from 'element-plus';

export default {
  name: 'Register',
  data() {
    return {
      form: {
        username: '',
        password: ''
      }
    };
  },
  methods: {
    goToLogin() {
      this.$router.push({name: 'Login'});
    },
    async register() {
      try {
        const params = new URLSearchParams();
        params.append('username', this.form.username);
        params.append('password', this.form.password);

        const response = await axios.post('http://127.0.0.1:8081/user/register', params);
        console.log("login response:", response);
        if (response.data.code === 200) {
          this.$message.success('注册成功');
          this.$router.push('/');
        } else {
          this.$message.error('注册失败');
          console.log(response.data.message)
        }
      } catch (error) {
        console.log(error);
      }
    }
  }
};
</script>

再定义一个简单的主页面,提供登录按钮,跳转到登录界面,以及登录或注册成功后可以导航到主页面。

<template>
  <h1>主页</h1>
  <div>
    <el-button @click="goToLogin">登录</el-button>
  </div>
</template>
  1. 定义路由,包括主页、登录页和注册页。我们还需要实现强制跳转登录页面的效果,所以这里通过在主页路由中添加 meta 字段 requiresAuth: true,表示访问主页需要认证。添加了全局前置守卫,确保在手动输入非登录页面的URL时,重定向到登录页面。
import {createRouter, createWebHistory} from 'vue-router';

import HomePage from '@/views/HomePage.vue';
import Login from "@/views/Login.vue";
import Register from "@/views/Register.vue";

const routes = [
{
        path: '/',
        name: 'HomePage',
        component: HomePage,
        meta: { requiresAuth: true } // 添加元信息,表示需要认证
    },
    {
        path: '/login',
        name: 'Login',
        component: Login
    },
    {
        path: '/register',
        name: 'Register',
        component: Register
    }
];   
  1. 定义根组件App,用于包裹整个Vue应用。引入路由,实现不同的路由显示不同的组件。
<template>
  <div id="app">

    <router-view/>

  </div>
</template>

<script>
import router from './router';

export default {
  name: 'App',
  components: {
    router
  }

};
</script>

5. 盐值(Salt):为增加密码安全性而随机添加的“调味料”

密码是用户访问各种服务和应用的钥匙。然而,密码的安全存储和管理是保护用户数据安全的关键。使用强加密算法来存储用户密码,是防止密码泄露和保护用户隐私的重要措施。

所以为了进一步提高密码存储的安全性,通常会在密码加密前添加一个随机生成的字符串,称为盐值(Salt)。盐值与密码一起进行哈希处理,确保即使两个用户使用相同的密码,生成的哈希值也不同。这可以防止攻击者使用彩虹表(Rainbow Table)等技术进行密码破解。

6. MD5加密算法:一种广泛使用的哈希函数,用于将任意长度的数据转换成固定长度的128位(16字节)哈希值

MD5是一种单向哈希函数,这意味着它可以将任意长度的数据映射为固定长度的哈希值,但无法从哈希值反向推导出原始数据。加盐(salt)的目的是为了增加破解的难度,即使两个用户使用了相同的密码,由于盐值不同,最终的哈希值也会不同。

7. 哈希函数:将任意长度的数据映射成固定长度数据的算法

密码加密通常使用哈希函数来实现。哈希函数是一种单向加密算法,可以将任意长度的输入转换成固定长度的输出。常见的哈希算法包括SHA-256、SHA-3等。哈希函数的主要特点是不可逆,即从哈希值无法直接恢复原始密码,从而增加了安全性。

8. 代码实现:使用强加密算法来存储用户密码,防止密码在数据库中以明文形式存储

8.1 后端功能实现

  1. 定义用户相关的控制层,提供/register接口,接收用户名和密码,调用 UserService 进行注册,返回注册结果。提供 /login 接口,接收用户名和密码,调用UserService进行密码验证,返回登录结果。所有接口都允许来自http://localhost:8080的跨域请求。
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @CrossOrigin(origins = "http://localhost:8080")
    @PostMapping("/register")
    public RestResult<String> register(@RequestParam String username, @RequestParam String password) {
        if (userService.registerUser(username, password)) {
            return RestResult.build("User registered successfully");
        } else {
            Map<String, Object> extension = new HashMap<>();
            extension.put("error", "User already exists");
            return RestResult.buildFailure(extension);
        }
    }

    @CrossOrigin(origins = "http://localhost:8080")
    @PostMapping("/login")
    public RestResult<String> login(@RequestParam String username, @RequestParam String password) {
        if (userService.verifyPassword(username, password)) {
            return RestResult.build("Login successful");
        } else {
            Map<String, Object> extension = new HashMap<>();
            extension.put("error", "Invalid username or password");
            return RestResult.buildFailure(extension);
        }
    }
}
  1. 定义用户服务实现类UserServiceImpl。提供了用于验证用户的密码的方法,先是通过用户名查找用户,然后使用hashPassword 方法对输入的密码进行哈希并与数据库中的密码哈希进行比较。还提供了用于注册新用户的方法,可以设置用户名、生成用户ID、生成盐值、哈希密码、设置随机用户名和注册时间,最后保存用户信息。generateSalt() 方法则用于生成盐值,使用SecureRandom生成随机字节数组,并将其编码为Base64字符串。hashPassword() 方法用于使用MD5算法对密码进行哈希,更新盐值,然后对密码进行哈希,并将结果编码为Base64字符串。
package com.example.interestplfm.service.impl;

import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.example.interestplfm.entity.User;
import com.example.interestplfm.mapper.UserMapper;
import com.example.interestplfm.repository.UserRepository;
import com.example.interestplfm.service.UserService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.Random;

@Service
public class UserServiceImpl implements UserService {

    @Resource
    private UserRepository userRepository;

    /**
     * 功能描述: 查找所有用户信息
     *
     * @param username 用户名
     * @param password 密码
     * @return boolean
     */
    @Override
    public boolean verifyPassword(String username, String password) {
        User user = userRepository.findByUsername(username);
        if (user == null) {
            return false;
        }
        String hashedPassword = hashPassword(password, user.getSalt());
        return hashedPassword.equals(user.getPasswordHash());
    }

    /**
     * 功能描述: 注册用户
     *
     * @param username 用户名
     * @param password 密码
     * @return boolean
     */
    @Override
    public boolean registerUser(String username, String password) {
        User user = new User();
        user.setUsername(username);
        // 设置盐值
        user.setUserid(IdWorker.getId());
        user.setUsername(username);
        String salt = generateSalt();
        user.setSalt(salt);
        user.setPasswordHash(hashPassword(password, salt));
        user.setPassword(password);
        Random random = new Random();
        user.setName("用户" + random.nextInt(100000));
        user.setRegisterTime(new Date());
        userRepository.save(user);
        return true;
    }

    /**
     * 功能描述:设置盐值
     *
     * @return {@code String }
     */
    private String generateSalt() {
        SecureRandom random = new SecureRandom();
        byte[] salt = new byte[16];
        random.nextBytes(salt);
        return Base64.getEncoder().encodeToString(salt);
    }

    /**
     * 功能描述:使用MD5算法加密密码
     *
     * @param password 密码
     * @param salt 盐值
     * @return {@code String }
     */
    private String hashPassword(String password, String salt) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(Base64.getDecoder().decode(salt));
            byte[] hashedPassword = md.digest(password.getBytes());
            return Base64.getEncoder().encodeToString(hashedPassword);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("MD5 algorithm not found", e);
        }
    }
}
  1. 根据表结构定义用户实体类
@Data
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    private String passwordHash;

    private String salt;

    private Long userid;

    private String name;

    private String sex;
    
    private int age;

    private String mailbox;

    private String password;

    private String username;

    private String headPortrait;

    private Date registerTime;

    private String introduce;

}

至此,一个简单的登录注册功能就已经实现完毕,可以强制用户进行认证登录,并且支持对用户注册的密码进行加盐加密,保证数据安全。

从页面操作上看,实现效果与我们预期的结果一致。
在这里插入图片描述
从数据层面看,注册的用户信息成功落库,同时密码加盐加密成功,再次输入密码登录也能校验成功。
在这里插入图片描述

9. 总结

强制用户登录和身份验证是保护敏感数据和系统安全的基石。随着技术的发展,我们需要不断评估和更新我们的方法,以应对日益复杂的安全威胁。通过采用多因素认证、自适应认证和其他先进的身份验证技术,我们可以为用户提供更安全、更便捷的访问体验。

密码加密是保护用户数据安全的重要环节。通过使用强加密算法、盐值、密钥拉伸技术和合理的密码策略,可以显著提高密码存储的安全性。同时,结合多因素认证和密码管理工具,可以为用户提供更全面的安全保护。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值