mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-04-21 03:17:28 +08:00
Add configurable password strength policy
This commit is contained in:
@@ -24,7 +24,7 @@ OpenIsle 基于 Spring Boot 构建,提供社区后台常见的注册、登录
|
|||||||
* **角色权限**:内置 `ADMIN` 与 `USER`,管理员接口以 `/api/admin/**` 提供
|
* **角色权限**:内置 `ADMIN` 与 `USER`,管理员接口以 `/api/admin/**` 提供
|
||||||
* **文章与评论**:支持分类、评论及多级回复
|
* **文章与评论**:支持分类、评论及多级回复
|
||||||
* **图片上传**:`ImageUploader` 可接入不同云存储,示例中实现了腾讯云 COS
|
* **图片上传**:`ImageUploader` 可接入不同云存储,示例中实现了腾讯云 COS
|
||||||
* **灵活配置**:数据库、邮件、存储等信息均可通过环境变量或 `application.properties` 设置
|
* **灵活配置**:数据库、邮件、存储及密码强度均可通过环境变量或 `application.properties` 设置
|
||||||
* **简洁架构**:业务、持久化与安全配置清晰分层,便于扩展
|
* **简洁架构**:业务、持久化与安全配置清晰分层,便于扩展
|
||||||
|
|
||||||
## 🚀 快速开始
|
## 🚀 快速开始
|
||||||
@@ -45,6 +45,7 @@ OpenIsle 基于 Spring Boot 构建,提供社区后台常见的注册、登录
|
|||||||
- `COS_BASE_URL`:腾讯云 COS 访问域名
|
- `COS_BASE_URL`:腾讯云 COS 访问域名
|
||||||
- `JWT_SECRET`:JWT 签名密钥
|
- `JWT_SECRET`:JWT 签名密钥
|
||||||
- `JWT_EXPIRATION`:JWT 过期时间(毫秒)
|
- `JWT_EXPIRATION`:JWT 过期时间(毫秒)
|
||||||
|
- `PASSWORD_STRENGTH`:密码强度(LOW、MEDIUM、HIGH)
|
||||||
2. 启动项目:
|
2. 启动项目:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
7
src/main/java/com/openisle/model/PasswordStrength.java
Normal file
7
src/main/java/com/openisle/model/PasswordStrength.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package com.openisle.model;
|
||||||
|
|
||||||
|
public enum PasswordStrength {
|
||||||
|
LOW,
|
||||||
|
MEDIUM,
|
||||||
|
HIGH
|
||||||
|
}
|
||||||
57
src/main/java/com/openisle/service/PasswordValidator.java
Normal file
57
src/main/java/com/openisle/service/PasswordValidator.java
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package com.openisle.service;
|
||||||
|
|
||||||
|
import com.openisle.model.PasswordStrength;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class PasswordValidator {
|
||||||
|
private final PasswordStrength strength;
|
||||||
|
|
||||||
|
public PasswordValidator(@Value("${app.password.strength:LOW}") PasswordStrength strength) {
|
||||||
|
this.strength = strength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validate(String password) {
|
||||||
|
if (password == null || password.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Password cannot be empty");
|
||||||
|
}
|
||||||
|
switch (strength) {
|
||||||
|
case MEDIUM:
|
||||||
|
checkMedium(password);
|
||||||
|
break;
|
||||||
|
case HIGH:
|
||||||
|
checkHigh(password);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// LOW, nothing beyond non-empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkMedium(String password) {
|
||||||
|
if (password.length() < 8) {
|
||||||
|
throw new IllegalArgumentException("Password must be at least 8 characters long");
|
||||||
|
}
|
||||||
|
if (!password.matches(".*[A-Za-z].*") || !password.matches(".*\\d.*")) {
|
||||||
|
throw new IllegalArgumentException("Password must contain letters and numbers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkHigh(String password) {
|
||||||
|
if (password.length() < 12) {
|
||||||
|
throw new IllegalArgumentException("Password must be at least 12 characters long");
|
||||||
|
}
|
||||||
|
if (!password.matches(".*[A-Z].*")) {
|
||||||
|
throw new IllegalArgumentException("Password must contain uppercase letters");
|
||||||
|
}
|
||||||
|
if (!password.matches(".*[a-z].*")) {
|
||||||
|
throw new IllegalArgumentException("Password must contain lowercase letters");
|
||||||
|
}
|
||||||
|
if (!password.matches(".*\\d.*")) {
|
||||||
|
throw new IllegalArgumentException("Password must contain numbers");
|
||||||
|
}
|
||||||
|
if (!password.matches(".*[^A-Za-z0-9].*")) {
|
||||||
|
throw new IllegalArgumentException("Password must contain special characters");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.openisle.service;
|
|||||||
|
|
||||||
import com.openisle.model.User;
|
import com.openisle.model.User;
|
||||||
import com.openisle.model.Role;
|
import com.openisle.model.Role;
|
||||||
|
import com.openisle.service.PasswordValidator;
|
||||||
import com.openisle.repository.UserRepository;
|
import com.openisle.repository.UserRepository;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
@@ -15,9 +16,11 @@ import java.util.Random;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class UserService {
|
public class UserService {
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
|
private final PasswordValidator passwordValidator;
|
||||||
private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||||
|
|
||||||
public User register(String username, String email, String password) {
|
public User register(String username, String email, String password) {
|
||||||
|
passwordValidator.validate(password);
|
||||||
// ── 先按用户名查 ──────────────────────────────────────────
|
// ── 先按用户名查 ──────────────────────────────────────────
|
||||||
Optional<User> byUsername = userRepository.findByUsername(username);
|
Optional<User> byUsername = userRepository.findByUsername(username);
|
||||||
if (byUsername.isPresent()) {
|
if (byUsername.isPresent()) {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ spring.jpa.hibernate.ddl-auto=update
|
|||||||
# for jwt
|
# for jwt
|
||||||
app.jwt.secret=${JWT_SECRET:ChangeThisSecretKeyForJwt}
|
app.jwt.secret=${JWT_SECRET:ChangeThisSecretKeyForJwt}
|
||||||
app.jwt.expiration=${JWT_EXPIRATION:86400000}
|
app.jwt.expiration=${JWT_EXPIRATION:86400000}
|
||||||
|
# Password strength: LOW, MEDIUM or HIGH
|
||||||
|
app.password.strength=${PASSWORD_STRENGTH:LOW}
|
||||||
|
|
||||||
# Post publish mode: DIRECT or REVIEW
|
# Post publish mode: DIRECT or REVIEW
|
||||||
app.post.publish-mode=${POST_PUBLISH_MODE:DIRECT}
|
app.post.publish-mode=${POST_PUBLISH_MODE:DIRECT}
|
||||||
|
|||||||
Reference in New Issue
Block a user