feat(security): 实现JWT认证与授权功能
重构用户登录服务,引入Spring Security和JWT认证机制 - 新增JwtUtils工具类处理JWT生成与验证 - 添加JwtAuthenticationFilter拦截请求验证token - 实现UserDetailsService从数据库加载用户信息 - 创建AuthController处理登录请求返回JWT - 重构用户角色权限相关接口,支持基于角色的访问控制 - 移除旧的安全配置,启用新的SecurityConfig - 新增LoginResponse DTO替代旧的LoginUser - 优化用户密码加密存储,使用BCryptPasswordEncoder
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
package com.qf.backend.service;
|
||||
|
||||
import com.qf.backend.common.Result;
|
||||
import com.qf.backend.dto.LoginUser;
|
||||
import com.qf.backend.dto.LoginResponse;
|
||||
|
||||
/**
|
||||
* 用户登录服务接口
|
||||
@@ -16,7 +16,7 @@ public interface UserLoginService {
|
||||
* 用户登录
|
||||
* @param username 用户名
|
||||
* @param password 密码
|
||||
* @return 登录结果
|
||||
* @return 登录结果,包含登录状态、token等信息
|
||||
*/
|
||||
Result<LoginUser> login(String username, String password);
|
||||
Result<LoginResponse> login(String username, String password);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.qf.backend.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.qf.backend.common.Result;
|
||||
import com.qf.backend.entity.UserRoles;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户角色关联服务接口
|
||||
*/
|
||||
|
||||
@@ -1,158 +1,87 @@
|
||||
package com.qf.backend.service.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.qf.backend.common.Result;
|
||||
import com.qf.backend.common.ResultUtils;
|
||||
import com.qf.backend.entity.UserDetails;
|
||||
import com.qf.backend.exception.BusinessException;
|
||||
import com.qf.backend.exception.ErrorCode;
|
||||
import com.qf.backend.mapper.UserDetailsMapper;
|
||||
import com.qf.backend.service.UserDetailsService;
|
||||
import com.qf.backend.entity.Roles;
|
||||
import com.qf.backend.entity.UserRoles;
|
||||
import com.qf.backend.entity.Users;
|
||||
import com.qf.backend.service.RolesService;
|
||||
import com.qf.backend.service.UserRolesService;
|
||||
import com.qf.backend.service.UsersService;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 用户详情服务实现类
|
||||
* UserDetailsService实现类,用于从数据库中加载用户信息
|
||||
* 该类实现了Spring Security的UserDetailsService接口,用于根据用户名加载用户信息
|
||||
*/
|
||||
@Service
|
||||
public class UserDetailsServiceImpl extends ServiceImpl<UserDetailsMapper, UserDetails> implements UserDetailsService {
|
||||
|
||||
public class UserDetailsServiceImpl implements UserDetailsService {
|
||||
|
||||
/**
|
||||
* 注入用户服务,用于查询用户信息
|
||||
*/
|
||||
@Autowired
|
||||
private UserDetailsMapper userDetailsMapper;
|
||||
private UsersService usersService;
|
||||
@Autowired
|
||||
private UserRolesService userRolesService;
|
||||
@Autowired
|
||||
private RolesService RolesService;
|
||||
|
||||
/**
|
||||
* 根据用户名加载用户信息
|
||||
* @param username 用户名
|
||||
* @return UserDetails 用户详情对象,包含用户名、密码、权限等信息
|
||||
* @throws UsernameNotFoundException 如果用户名不存在
|
||||
*/
|
||||
@Override
|
||||
public Result<UserDetails> getUserDetailsByUserId(Long userId) {
|
||||
if (userId == null) {
|
||||
throw new BusinessException(ErrorCode.MISSING_PARAM, "用户ID不能为空");
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
// 1. 从数据库中查询用户
|
||||
Result<Users> result = usersService.getUserByUsername(username);
|
||||
if (result == null || result.getData() == null) {
|
||||
throw new UsernameNotFoundException("用户名不存在: " + username);
|
||||
}
|
||||
try {
|
||||
UserDetails userDetails = userDetailsMapper.selectOne(
|
||||
new QueryWrapper<UserDetails>().eq("user_id", userId));
|
||||
return ResultUtils.success(userDetails);
|
||||
} catch (BusinessException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException(ErrorCode.DATABASE_ERROR, "查询用户详情失败", e);
|
||||
|
||||
Users user = result.getData();
|
||||
|
||||
// 2. 构建权限列表(这里简化处理,实际应从数据库中查询用户的角色和权限)
|
||||
List<GrantedAuthority> authorities = new ArrayList<>();
|
||||
// 查询用户角色关联
|
||||
Result<List<UserRoles>> userRoleResultList = userRolesService.getUserRolesByUserId(user.getId());
|
||||
if (userRoleResultList == null || userRoleResultList.getData() == null) {
|
||||
throw new UsernameNotFoundException("用户角色不存在: " + user.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Boolean> createUserDetails(UserDetails userDetails) {
|
||||
if (userDetails == null) {
|
||||
throw new BusinessException(ErrorCode.MISSING_PARAM, "用户详情信息不能为空");
|
||||
}
|
||||
if (userDetails.getUserId() == null) {
|
||||
throw new BusinessException(ErrorCode.MISSING_PARAM, "用户ID不能为空");
|
||||
}
|
||||
try {
|
||||
// 检查是否已存在该用户的详情
|
||||
UserDetails existing = userDetailsMapper.selectOne(
|
||||
new QueryWrapper<UserDetails>().eq("user_id", userDetails.getUserId()));
|
||||
if (existing != null) {
|
||||
throw new BusinessException(ErrorCode.BUSINESS_ERROR, "该用户已存在详情信息");
|
||||
// 3. 查询角色权限
|
||||
for (UserRoles userRole : userRoleResultList.getData()) {
|
||||
Result<Roles> roleResult = RolesService.getRoleById(userRole.getRoleId());
|
||||
if (roleResult == null || roleResult.getData() == null) {
|
||||
throw new UsernameNotFoundException("权限不存在: " + userRole.getRoleId());
|
||||
}
|
||||
int result = userDetailsMapper.insert(userDetails);
|
||||
return ResultUtils.success(result > 0);
|
||||
} catch (BusinessException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException(ErrorCode.DATABASE_ERROR, "创建用户详情失败", e);
|
||||
Roles role = roleResult.getData();
|
||||
// 4. 转换为Spring Security的GrantedAuthority对象
|
||||
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleName().toUpperCase()));
|
||||
}
|
||||
// 3. 返回UserDetails对象
|
||||
// 注意:在实际应用中,密码应该加密存储,这里直接使用明文密码(仅用于演示)
|
||||
return User.builder()
|
||||
.username(user.getUsername()) // 用户名
|
||||
.password(user.getPassword()) // 密码需要加密存储,这里直接使用明文密码(仅用于演示)
|
||||
.authorities(authorities) // 假设用户默认拥有USER权限
|
||||
.accountExpired(false) // 假设账号永不过期
|
||||
.accountLocked(false) // 假设账号永不过期
|
||||
.credentialsExpired(false) // 假设密码永不过期
|
||||
.disabled(user.getStatus() == 0) // 假设status为0表示禁用
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Boolean> updateUserDetails(UserDetails userDetails) {
|
||||
if (userDetails == null) {
|
||||
throw new BusinessException(ErrorCode.MISSING_PARAM, "用户详情信息不能为空");
|
||||
}
|
||||
if (userDetails.getId() == null) {
|
||||
throw new BusinessException(ErrorCode.MISSING_PARAM, "用户详情ID不能为空");
|
||||
}
|
||||
try {
|
||||
// 检查用户详情是否存在
|
||||
UserDetails existing = userDetailsMapper.selectById(userDetails.getId());
|
||||
if (existing == null) {
|
||||
throw new BusinessException(ErrorCode.NOT_FOUND, "用户详情不存在");
|
||||
}
|
||||
int result = userDetailsMapper.updateById(userDetails);
|
||||
return ResultUtils.success(result > 0);
|
||||
} catch (BusinessException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException(ErrorCode.DATABASE_ERROR, "更新用户详情失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Boolean> deleteUserDetailsByUserId(Long userId) {
|
||||
if (userId == null) {
|
||||
throw new BusinessException(ErrorCode.MISSING_PARAM, "用户ID不能为空");
|
||||
}
|
||||
try {
|
||||
// 检查用户详情是否存在
|
||||
UserDetails existing = userDetailsMapper.selectOne(
|
||||
new QueryWrapper<UserDetails>().eq("user_id", userId));
|
||||
if (existing == null) {
|
||||
throw new BusinessException(ErrorCode.NOT_FOUND, "用户详情不存在");
|
||||
}
|
||||
int result = userDetailsMapper.delete(
|
||||
new QueryWrapper<UserDetails>().eq("user_id", userId));
|
||||
return ResultUtils.success(result > 0);
|
||||
} catch (BusinessException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException(ErrorCode.DATABASE_ERROR, "删除用户详情失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<UserDetails> getUserDetailsById(Long id) {
|
||||
if (id == null) {
|
||||
throw new BusinessException(ErrorCode.MISSING_PARAM, "用户详情ID不能为空");
|
||||
}
|
||||
try {
|
||||
UserDetails userDetails = userDetailsMapper.selectById(id);
|
||||
if (userDetails == null) {
|
||||
throw new BusinessException(ErrorCode.NOT_FOUND, "用户详情不存在");
|
||||
}
|
||||
return ResultUtils.success(userDetails);
|
||||
} catch (BusinessException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException(ErrorCode.DATABASE_ERROR, "查询用户详情失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public Result<Boolean> updateContactInfo(Long userId, String phone, String email) {
|
||||
// if (userId == null) {
|
||||
// throw new BusinessException(ErrorCode.MISSING_PARAM, "用户ID不能为空");
|
||||
// }
|
||||
// if (ValidateUtil.isEmpty(phone) && ValidateUtil.isEmpty(email)) {
|
||||
// throw new BusinessException(ErrorCode.MISSING_PARAM, "手机号和邮箱不能同时为空");
|
||||
// }
|
||||
// try {
|
||||
// // 检查用户详情是否存在
|
||||
// UserDetails existing = userDetailsMapper.selectOne(
|
||||
// new QueryWrapper<UserDetails>().eq("user_id", userId));
|
||||
// if (existing == null) {
|
||||
// throw new BusinessException(ErrorCode.NOT_FOUND, "用户详情不存在");
|
||||
// }
|
||||
// // 更新联系方式
|
||||
// if (phone != null) {
|
||||
// existing.setPhone(phone);
|
||||
// }
|
||||
// if (email != null) {
|
||||
// existing.setEmail(email);
|
||||
// }
|
||||
// int result = userDetailsMapper.updateById(existing);
|
||||
// return ResultUtils.success(result > 0);
|
||||
// } catch (BusinessException e) {
|
||||
// throw e;
|
||||
// } catch (Exception e) {
|
||||
// throw new BusinessException(ErrorCode.DATABASE_ERROR, "更新用户联系方式失败", e);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import com.qf.backend.common.Result;
|
||||
import com.qf.backend.common.ResultUtils;
|
||||
import com.qf.backend.dto.LoginUser;
|
||||
import com.qf.backend.dto.LoginResponse;
|
||||
import com.qf.backend.entity.Permissions;
|
||||
import com.qf.backend.entity.Roles;
|
||||
import com.qf.backend.entity.UserRoles;
|
||||
@@ -57,17 +57,15 @@ public class UserLoginServiceImpl implements UserLoginService {
|
||||
* @return 登录结果
|
||||
*/
|
||||
@Override
|
||||
public Result<LoginUser> login(String username, String password) {
|
||||
public Result<LoginResponse> login(String username, String password) {
|
||||
logger.info("用户登录,用户名:{}", username);
|
||||
// 1. 校验用户名和密码是否为空
|
||||
try{
|
||||
if (ValidateUtil.isEmpty(username) || ValidateUtil.isEmpty(password)) {
|
||||
throw new IllegalArgumentException("用户名或密码不能为空");
|
||||
}
|
||||
// 加密密码
|
||||
String encryptedPassword = ValidateUtil.encryptPassword(password);
|
||||
// 2. 登录
|
||||
Result<Users> result = usersServiceImpl.login(username, encryptedPassword);
|
||||
Result<Users> result = usersServiceImpl.login(username, password);
|
||||
if (result == null || result.getData() == null) {
|
||||
throw new IllegalArgumentException("用户名不存在或密码错误");
|
||||
}
|
||||
@@ -87,7 +85,7 @@ public class UserLoginServiceImpl implements UserLoginService {
|
||||
for (UserRoles ur : userRoles) {
|
||||
Roles role = rolesServiceImpl.getById(ur.getRoleId());
|
||||
if (role != null) {
|
||||
roleNames.add(role.getRoleName());
|
||||
roleNames.add(String.valueOf(role.getRoleType()));
|
||||
roleIds.add(role.getId());
|
||||
}
|
||||
}
|
||||
@@ -104,14 +102,12 @@ public class UserLoginServiceImpl implements UserLoginService {
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 构建LoginUser对象
|
||||
LoginUser loginUser = new LoginUser();
|
||||
loginUser.setId(user.getId());
|
||||
loginUser.setUsername(user.getUsername());
|
||||
loginUser.setRoles(new ArrayList<>(roleNames));
|
||||
loginUser.setPermissions(new ArrayList<>(permissionCodes));
|
||||
|
||||
return ResultUtils.success(loginUser);
|
||||
// 6. 构建LoginResponse对象
|
||||
LoginResponse loginResponse = new LoginResponse();
|
||||
loginResponse.setUsername(user.getUsername());
|
||||
loginResponse.setRoles(new ArrayList<>(roleNames));
|
||||
loginResponse.setPermissions(new ArrayList<>(permissionCodes));
|
||||
return ResultUtils.success(loginResponse);
|
||||
} catch (Exception e) {
|
||||
logger.error("用户登录失败,用户名:{}", username, e);
|
||||
return ResultUtils.fail(ErrorCode.SYSTEM_ERROR, e.getMessage());
|
||||
|
||||
@@ -88,12 +88,18 @@ public class UsersServiceImpl extends ServiceImpl<UsersMapper, Users> implements
|
||||
throw new BusinessException(ErrorCode.INVALID_PARAM, "用户名或密码不能为空");
|
||||
}
|
||||
|
||||
// 校验用户名是否存在
|
||||
Users user = usersMapper.login(username, password);
|
||||
// 根据用户名查询用户
|
||||
Users user = usersMapper.selectByUsername(username);
|
||||
if (user == null) {
|
||||
throw new BusinessException(ErrorCode.USER_NOT_FOUND, "用户名不存在或密码错误");
|
||||
}
|
||||
|
||||
// 使用BCryptPasswordEncoder验证密码
|
||||
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||
if (!passwordEncoder.matches(password, user.getPassword())) {
|
||||
throw new BusinessException(ErrorCode.USER_NOT_FOUND, "用户名不存在或密码错误");
|
||||
}
|
||||
|
||||
return ResultUtils.success(user);
|
||||
} catch (BusinessException e) {
|
||||
throw e;
|
||||
|
||||
Reference in New Issue
Block a user