feat: 实现基于JSON的登录API和安全认证
refactor: 重构登录逻辑和会话管理 fix: 修复会话ID类型和WebSocket连接问题 build: 更新项目版本号和添加Servlet API依赖 style: 清理无用导入和注释代码
This commit is contained in:
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>com.ecep.contract</groupId>
|
||||
<artifactId>Contract-Manager</artifactId>
|
||||
<version>0.0.58-SNAPSHOT</version>
|
||||
<version>0.0.80-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.ecep.contract</groupId>
|
||||
<artifactId>server</artifactId>
|
||||
<version>0.0.58-SNAPSHOT</version>
|
||||
<version>0.0.80-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
@@ -22,7 +22,7 @@
|
||||
<dependency>
|
||||
<groupId>com.ecep.contract</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>0.0.58-SNAPSHOT</version>
|
||||
<version>0.0.80-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -74,6 +74,14 @@
|
||||
<!-- <artifactId>hibernate-jpamodelgen</artifactId> -->
|
||||
<!-- <scope>provided</scope> -->
|
||||
<!-- </dependency> -->
|
||||
|
||||
<!-- Servlet API -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>4.0.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
package com.ecep.contract.api.controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.ecep.contract.ds.other.service.EmployeeAuthBindService;
|
||||
import com.ecep.contract.ds.other.service.EmployeeService;
|
||||
import com.ecep.contract.model.Employee;
|
||||
import com.ecep.contract.model.EmployeeAuthBind;
|
||||
|
||||
import lombok.Data;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api")
|
||||
public class LoginApiController {
|
||||
private static final Logger logger = LoggerFactory.getLogger(LoginApiController.class);
|
||||
|
||||
@Autowired
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
@Autowired
|
||||
private EmployeeService employeeService;
|
||||
@Autowired
|
||||
private EmployeeAuthBindService employeeAuthBindService;
|
||||
|
||||
/**
|
||||
* JSON格式的登录API
|
||||
* 使用Spring Security进行认证,返回JSON格式的响应
|
||||
*/
|
||||
@PostMapping("/login")
|
||||
public Map<String, Object> jsonLogin(@RequestBody LoginRequest loginRequest, HttpServletRequest request) {
|
||||
// 获取HttpSession
|
||||
HttpSession session = request.getSession();
|
||||
|
||||
// 添加日志记录,确认方法被调用
|
||||
logger.debug("jsonLogin方法被调用,请求类型: {}", loginRequest.getType());
|
||||
logger.debug("请求用户名: {}", loginRequest.getUsername());
|
||||
logger.debug("会话ID: {}", session != null ? session.getId() : "null");
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
try {
|
||||
Employee employee = null;
|
||||
if ("client".equals(loginRequest.getType())) {
|
||||
// 根据用户名查找Employee对象
|
||||
employee = employeeService.findByAccount(loginRequest.getUsername());
|
||||
if (employee == null) {
|
||||
result.put("success", false);
|
||||
result.put("error", "用户信息不存在");
|
||||
return result;
|
||||
}
|
||||
List<EmployeeAuthBind> authBinds = employeeAuthBindService.findAllByEmployee(employee, Sort.unsorted());
|
||||
if (authBinds.isEmpty()) {
|
||||
result.put("success", false);
|
||||
result.put("error", "用户未绑定认证信息");
|
||||
return result;
|
||||
}
|
||||
|
||||
EmployeeAuthBind matched = null;
|
||||
Map<String, String> signMap = loginRequest.getSign();
|
||||
if (signMap != null) {
|
||||
for (EmployeeAuthBind authBind : authBinds) {
|
||||
String clientIp = signMap.get(authBind.getMac());
|
||||
if (clientIp != null && authBind.getIp().equals(clientIp)) {
|
||||
matched = authBind;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (matched == null) {
|
||||
result.put("success", false);
|
||||
result.put("error", "认证信息错误");
|
||||
return result;
|
||||
}
|
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
|
||||
loginRequest.getUsername(), "default123");
|
||||
|
||||
// 使用AuthenticationManager进行认证
|
||||
Authentication authentication = authenticationManager.authenticate(authenticationToken);
|
||||
|
||||
// 设置认证结果到SecurityContext
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
|
||||
} else {
|
||||
// 创建认证令牌
|
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
|
||||
loginRequest.getUsername(), loginRequest.getPassword());
|
||||
|
||||
// 使用AuthenticationManager进行认证
|
||||
Authentication authentication = authenticationManager.authenticate(authenticationToken);
|
||||
|
||||
// 设置认证结果到SecurityContext
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
|
||||
employee = employeeService.findByAccount(loginRequest.getUsername());
|
||||
}
|
||||
|
||||
if (employee != null) {
|
||||
// 获取当前登录用户
|
||||
User currentUser = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
// 返回登录成功的信息
|
||||
result.put("success", true);
|
||||
result.put("employeeId", employee.getId());
|
||||
result.put("sessionId", session.getId());
|
||||
result.put("username", employee.getAccount());
|
||||
result.put("roles", currentUser.getAuthorities());
|
||||
result.put("message", "登录成功");
|
||||
} else {
|
||||
// 用户不存在
|
||||
result.put("success", false);
|
||||
result.put("error", "用户信息不存在");
|
||||
}
|
||||
|
||||
} catch (AuthenticationException e) {
|
||||
// 认证失败
|
||||
result.put("success", false);
|
||||
result.put("error", "用户名或密码错误");
|
||||
} catch (Exception e) {
|
||||
// 其他错误
|
||||
result.put("success", false);
|
||||
result.put("error", "登录过程中发生错误: " + e.getMessage());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录请求的JSON数据模型
|
||||
*/
|
||||
@Data
|
||||
public static class LoginRequest {
|
||||
private String type;
|
||||
private String username;
|
||||
private String password;
|
||||
|
||||
private Map<String, String> sign = new HashMap<>();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -15,13 +15,15 @@ import org.springframework.web.method.annotation.MethodArgumentTypeMismatchExcep
|
||||
import org.springframework.web.servlet.NoHandlerFoundException;
|
||||
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 全局异常处理器,捕获并处理所有Controller层抛出的异常,将错误信息以JSON格式返回给前端
|
||||
*/
|
||||
// @RestControllerAdvice
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||
@@ -36,9 +38,24 @@ public class GlobalExceptionHandler {
|
||||
result.put("code", 500);
|
||||
result.put("message", "系统内部错误:" + e.getMessage());
|
||||
result.put("errorType", e.getClass().getName());
|
||||
result.put("stackTrace", getStackTraceAsString(e));
|
||||
result.put("success", false);
|
||||
return new ResponseEntity<>(result, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将异常堆栈转换为字符串
|
||||
*/
|
||||
private String getStackTraceAsString(Throwable throwable) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
try {
|
||||
throwable.printStackTrace(pw);
|
||||
return sw.toString();
|
||||
} finally {
|
||||
pw.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理运行时异常
|
||||
@@ -50,6 +67,7 @@ public class GlobalExceptionHandler {
|
||||
result.put("code", 500);
|
||||
result.put("message", "运行时错误:" + e.getMessage());
|
||||
result.put("errorType", e.getClass().getName());
|
||||
result.put("stackTrace", getStackTraceAsString(e));
|
||||
result.put("success", false);
|
||||
return new ResponseEntity<>(result, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
@@ -51,8 +51,8 @@ public class SecurityConfig {
|
||||
http
|
||||
.authorizeHttpRequests(authorize -> authorize
|
||||
.requestMatchers("/login.html", "/css/**", "/js/**", "/images/**", "/webjars/**", "/login",
|
||||
"/error")
|
||||
.permitAll() // 允许静态资源、登录页面和错误页面访问
|
||||
"/error", "/api/login")
|
||||
.permitAll() // 允许静态资源、登录页面、错误页面和JSON登录API访问
|
||||
.anyRequest().authenticated() // 其他所有请求需要认证
|
||||
)
|
||||
.csrf(AbstractHttpConfigurer::disable) // 禁用CSRF保护,适合开发环境
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.ecep.contract.ds.other.controller;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
@@ -8,6 +11,7 @@ import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@@ -50,4 +54,35 @@ public class EmployeeController {
|
||||
employeeService.delete(employee);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户的信息
|
||||
* 包括employeeId和sessionId
|
||||
*/
|
||||
@RequestMapping("/currentUser")
|
||||
public Map<String, Object> getCurrentUser(HttpSession session) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
try {
|
||||
// 获取当前登录用户
|
||||
User currentUser = SecurityUtils.getCurrentUser();
|
||||
if (currentUser != null) {
|
||||
// 根据用户名查找Employee对象
|
||||
Employee employee = employeeService.findByName(currentUser.getUsername());
|
||||
if (employee != null) {
|
||||
result.put("employeeId", employee.getId());
|
||||
result.put("sessionId", session.getId());
|
||||
result.put("success", true);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 处理异常
|
||||
}
|
||||
|
||||
// 如果获取失败,返回错误信息
|
||||
result.put("success", false);
|
||||
result.put("error", "无法获取当前用户信息");
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,21 +4,14 @@ import java.util.List;
|
||||
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.ecep.contract.ds.MyRepository;
|
||||
import com.ecep.contract.model.Employee;
|
||||
import com.ecep.contract.model.EmployeeAuthBind;
|
||||
|
||||
@Lazy
|
||||
@Repository
|
||||
public interface EmployeeAuthBindRepository extends
|
||||
// JDBC interfaces
|
||||
CrudRepository<EmployeeAuthBind, Integer>, PagingAndSortingRepository<EmployeeAuthBind, Integer>,
|
||||
// JPA interfaces
|
||||
JpaRepository<EmployeeAuthBind, Integer>, JpaSpecificationExecutor<EmployeeAuthBind> {
|
||||
public interface EmployeeAuthBindRepository extends MyRepository<EmployeeAuthBind, Integer> {
|
||||
List<EmployeeAuthBind> findAllByEmployee(Employee employee, Sort sort);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user