在当今的数字化世界中,保护用户数据安全是至关重要的。在本文中,我们将探讨如何在Spring Boot后端中实现密码加密,并且给出前端参考代码。
密码加密的重要性
在用户认证系统中,密码的安全存储是至关重要的。明文存储密码是极其危险的做法,一旦数据库被泄露,用户的密码也会随之暴露。使用强哈希函数对密码进行加密可以大大提高安全性。
后端实现
1. 工具类
package com.example.javaee.config;
import org.springframework.security.crypto.bcrypt.BCrypt;
public class BCryptUtil {
/**
* 密码加密
* @param rawPassword 原始密码
* @return 加密后的密码
*/
public static String hashPassword(String rawPassword) {
return BCrypt.hashpw(rawPassword, BCrypt.gensalt());
}
/**
* 验证密码是否匹配
* @param rawPassword 原始密码
* @param hashedPassword 加密后的密码
* @return 密码是否匹配
*/
public static boolean checkPassword(String rawPassword, String hashedPassword) {
return BCrypt.checkpw(rawPassword, hashedPassword);
}
}
2.登陆注册方法
package com.example.javaee.service;
import com.example.javaee.config.JwtUtil;
import com.example.javaee.entity.Response;
import com.example.javaee.entity.User;
import com.example.javaee.mapper.UserMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public Response login(String account, String password) {
User existingUser = userMapper.findByUsername(account);
if (existingUser != null && existingUser.checkPassword(password)) {
String token = JwtUtil.generateToken(account);
String permission = existingUser.getPermission();
return new Response("登录成功", token, permission);
}
return new Response("登录失败", null, null);
}
public String register(String username, Integer studentId, String password) {
// 检查用户名是否已存在
User existingUser = userMapper.findByUsername(username);
if (existingUser != null) {
return "注册失败,用户名已存在";
}
// 创建新的 User 对象
User user = new User();
user.setUsername(username);
user.setStudentId(studentId);
// 使用 BCrypt 加密密码
String encryptedPassword = BCrypt.hashpw(password, BCrypt.gensalt());
user.setPassword(encryptedPassword);
// 将新用户插入数据库
int result = userMapper.insertUser(user);
if (result > 0) {
return "注册成功";
} else {
return "注册失败";
}
}
public String addUser(String username, String password, String permission, Integer studentId) {
User user = new User();
user.setUsername(username);
// 使用 BCrypt 加密密码
String encryptedPassword = BCrypt.hashpw(password, BCrypt.gensalt());
user.setPassword(encryptedPassword);
user.setPermission(permission);
user.setStudentId(studentId);
int i = userMapper.insertUser2(user);
if (i > 0) {
return "添加成功";
} else {
return "添加失败,用户名已存在";
}
}
}
前端实现
1. 登录页面
<template>
<div id="login">
<div class="me-login-box me-login-box-radius">
<h1>登录界面</h1>
<el-form ref="userForm" :model="userForm" :rules="rules">
<el-form-item prop="account">
<div class="my-form1">
<img src="../assets/img/user.png" id="user-img">
<input class="my-input" placeholder="用户名" v-model="userForm.account" ref="account" />
</div>
</el-form-item>
<el-form-item prop="password">
<div class="my-form1">
<img src="../assets/img/lock.png" id="password-img">
<input class="my-input" placeholder="密码" type="password" v-model="userForm.password" ref="password"
@keyup="onHCapitalize($event)" />
<img src="../assets/img/eyesclosed.png" id="eyes-img" ref="eyes" @click="show()">
</div>
</el-form-item>
<el-form-item size="small" class="me-login-button">
<el-button type="primary" @click.native.prevent="login('userForm')">登录</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import { login } from '../api/api'
import { TOKEN_KEY } from '../router/index'
export default {
name: 'Login',
data() {
return {
userForm: {
account: '',
password: ''
},
rules: {
account: [
{ required: true, message: '用户名不能为空' }
],
password: [
{ required: true, message: '密码不能为空' },
{ min: 6, message: 'password must be at least 6 characters' }
]
},
flag: 'false',
bigChar: 'false'
}
},
methods: {
login(formName) {
let that = this;
this.$refs[formName].validate((valid) => {
if (valid) {
if (this.userForm.account.trim() !== "" && this.userForm.password.trim() !== "") {
let params = new URLSearchParams();
params.append('account', this.userForm.account);
params.append('password', this.userForm.password);
login(params)
.then(function (response) {
if (response.data.message === '登录成功') {
const token = response.data.token;
// 存储 Token 到 localStorage
localStorage.setItem('authToken', token);
// 根据返回的权限进行路由跳转
switch (response.data.permission) {
case '超级管理员':
that.$router.push({ name: 'superadmin' });
break;
case '普通管理员':
that.$router.push({ name: 'admin' });
break;
case '注册用户':
that.$router.push({ name: 'user' });
break;
default:
console.error('未知权限');
}
} else if (responseData.message === '登录失败') {
console.error('登录失败,用户名不存在或者密码错误');
}
})
.catch(function (error) {
console.error(error);
});
} else {
// 验证账号密码是否为空,并设置输入框边框颜色
if (this.userForm.account.trim() === "") {
this.$refs.account.style.borderColor = "red";
} else {
this.$refs.account.style.borderColor = "#797979";
}
if (this.userForm.password.trim() === "") {
this.$refs.password.style.borderColor = "red";
} else {
this.$refs.password.style.borderColor = "#797979";
}
return false;
}
}
});
},
show() {
if (this.flag) {
this.$refs.eyes.src = "../../static/img/eyes.png";
this.$refs.password.setAttribute("type", "text");
} else {
this.$refs.eyes.src = "../../static/img/eyesclosed.png";
this.$refs.password.setAttribute("type", "password");
}
this.flag = !this.flag;
},
}
}
</script>
<style>
</style>
2. 注册页面
<template>
<div id="register">
<div class="me-register-box me-register-box-radius">
<h1>注册界面</h1>
<el-form ref="registerForm" :model="registerForm" :rules="rules">
<el-form-item prop="username">
<div class="my-form1">
<img src="../assets/img/user.png" id="user-img">
<input class="my-input" placeholder="用户名" v-model="registerForm.username" ref="username" />
</div>
</el-form-item>
<el-form-item prop="studentId">
<div class="my-form1">
<img src="../assets/img/id.png" id="password-img">
<input class="my-input" placeholder="学号" v-model="registerForm.studentId" ref="studentId" />
</div>
</el-form-item>
<el-form-item prop="password">
<div class="my-form1">
<img src="../assets/img/lock.png" id="password-img">
<input class="my-input" placeholder="密码" type="password" v-model="registerForm.password" ref="password" />
</div>
</el-form-item>
<el-form-item size="small" class="me-register-button">
<el-button type="primary" @click.native.prevent="register('registerForm')">注册</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import { registerUser } from '../api/api';
export default {
name: 'register',
data() {
return {
registerForm: {
username: '',
studentId: '',
password: ''
},
rules: {
username: [
{ required: true, message: '用户名不能为空!' }
],
studentId: [
{ required: true, message: '学号不能为空!' },
{ min: 9, message: '学号至少由九位组成!' }
],
password: [
{ required: true, message: '密码不能为空!' },
{ min: 6, message: '密码至少由六位组成!' }
]
}
}
},
methods: {
register(formName) {
let that = this;
this.$refs[formName].validate((valid) => {
if (valid) {
if (this.registerForm.username.trim() != "" && this.registerForm.password.trim() != "") {
let params = new URLSearchParams();
params.append("username", this.registerForm.username);
params.append("studentId", this.registerForm.studentId);
params.append("password", this.registerForm.password);
registerUser(params)
.then(function (response) {
console.log(response);
if (response.data === "注册成功") {
that.$router.push({ name: "Home" });
} else if (response.data === "注册失败") {
alert("注册失败,用户名已存在");
}
})
.catch(function (error) {
console.log(error);
});
}
} else {
if (this.registerForm.username.trim() === "") {
this.$refs.username.style.borderColor = "red";
} else {
this.$refs.username.style.borderColor = "#797979";
}
if (this.registerForm.password.trim() === "") {
this.$refs.password.style.borderColor = "red";
} else {
this.$refs.password.style.borderColor = "#797979";
}
return false;
}
});
},
},
}
</script>
<style scoped>
</style>
测试和调试
进行测试是必不可少的,确保密码加密正确无误。
package com.example.javaee;
import com.example.javaee.config.BCryptUtil;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.security.crypto.bcrypt.BCrypt;
public class BCryptUtilTest {
@Test
public void testPasswordMatching() {
// 原始密码
String rawPassword = "123456";
// 使用BCryptUtil加密原始密码
String hashedPassword = BCryptUtil.hashPassword(rawPassword);
// 打印加密前的密码和加密后的密码
System.out.println("原始密码: " + rawPassword);
System.out.println("加密后的密码: " + hashedPassword);
// 验证原始密码与加密后的密码是否匹配
boolean isMatch = BCryptUtil.checkPassword(rawPassword, hashedPassword);
// 打印密码匹配结果
System.out.println("密码匹配结果: " + isMatch);
// 断言密码匹配结果为true
Assert.assertTrue(isMatch);
}
@Test
public void testPasswordNotMatching() {
// 原始密码
String rawPassword = "123456";
// 故意使用错误的密码进行测试
String wrongPassword = "654321";
// 加密原始密码
String hashedPassword = BCryptUtil.hashPassword(rawPassword);
// 打印加密前的密码和加密后的密码
System.out.println("原始密码: " + rawPassword);
System.out.println("加密后的密码: " + hashedPassword);
// 验证错误的密码与加密后的密码是否匹配
boolean isMatch = BCryptUtil.checkPassword(wrongPassword, hashedPassword);
// 打印密码匹配结果
System.out.println("密码匹配结果: " + isMatch);
// 断言密码匹配结果为false
Assert.assertFalse(isMatch);
}
}
结论
保护用户数据是每个开发者的责任,使用哈希密码可以大大降低安全风险。