This commit is contained in:
2026-04-30 16:27:58 +08:00
parent ac6442a452
commit 2adefb5207
16 changed files with 284 additions and 349 deletions
@@ -2,6 +2,8 @@ package com.yaoyuan.jiscuss.common;
import com.yaoyuan.jiscuss.entity.custom.PostCustom;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import java.util.ArrayList;
import java.util.Date;
@@ -17,6 +19,8 @@ import java.util.List;
@Data
public class Node {
private static final Logger logger = LoggerFactory.getLogger(Node.class);
/**
* Node
* 空方法
@@ -85,7 +89,7 @@ public class Node {
for (Node node1 : list) { //循环添加
if (node1.getId().equals(node.getParentId())) { //判断留言的上一段是都是这条留言
node1.getNextNodes().add(node); //是,添加,返回true;
System.out.println("添加了一个");
logger.debug("添加了一个");
return true;
} else { //否则递归继续判断
if (node1.getNextNodes().size() != 0) {
@@ -127,7 +131,7 @@ public class Node {
**/
public static void show(List<Node> list) {
for (Node node : list) {
System.out.println(node.getUserId() + " 用户回复了你:" + node.getContent());
logger.debug("{} 用户回复了你:{}", node.getUserId(), node.getContent());
if (node.getNextNodes().size() != 0) {
Node.show(node.getNextNodes());
}
@@ -16,7 +16,7 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.access.expression.DefaultHttpSecurityExpressionHandler;
import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;
/**
@@ -69,7 +69,7 @@ public class WebSecurityConfig {
* <p>Security decisions:
* <ul>
* <li>H2 console: only accessible when enabled (dev profile) and requires ROLE_ADMIN</li>
* <li>Druid monitoring: requires ROLE_ADMIN (Druid also has its own login page)</li>
* <li>Druid monitoring: requires ROLE_ADMIN through Spring Security</li>
* <li>Admin/Actuator paths: requires ROLE_ADMIN</li>
* <li>SpringDoc UI paths: public (API docs are for developers)</li>
* <li>All other authenticated requests: evaluated by {@code RbacPermission.hasPermission}</li>
@@ -89,7 +89,7 @@ public class WebSecurityConfig {
}
// Build the RBAC expression manager with ApplicationContext so @bean references work
DefaultWebSecurityExpressionHandler expressionHandler = new DefaultWebSecurityExpressionHandler();
DefaultHttpSecurityExpressionHandler expressionHandler = new DefaultHttpSecurityExpressionHandler();
expressionHandler.setApplicationContext(applicationContext);
WebExpressionAuthorizationManager rbacManager = new WebExpressionAuthorizationManager(
"@rbacPermission.hasPermission(request, authentication)");
@@ -1,48 +1,34 @@
package com.yaoyuan.jiscuss.controller;
import com.yaoyuan.jiscuss.entity.UserInfo;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import java.util.Enumeration;
/**
* Base controller exposing helpers shared by web controllers.
*
* @author yaoyuan2.chu
* @Title: BaseController
* @Package com.yaoyuan.jiscuss.controller
* @Description: BaseController
* @date 2020/7/16 14:36
*/
public class BaseController {
/**
* 获取当前用户
*
* @return
* Returns the current authenticated user, or {@code null} when not logged in.
*/
protected UserInfo getUserInfo(HttpServletRequest request) {
UserInfo user = null;
//获得session对象
HttpSession session = request.getSession();
//取出session域中所有属性名
Enumeration attributeNames = session.getAttributeNames();
while (attributeNames.hasMoreElements()) {
System.out.println(attributeNames.nextElement());
HttpSession session = request.getSession(false);
if (session == null) {
return null;
}
//SPRING_SECURITY_CONTEXT
Object spring_security_context = session.getAttribute("SPRING_SECURITY_CONTEXT");
System.out.println(spring_security_context);
SecurityContext securityContext = (SecurityContext) spring_security_context;
if (securityContext != null) {
//获得认证信息
Authentication authentication = securityContext.getAuthentication();
//获得用户详情
Object principal = authentication.getPrincipal();
user = (UserInfo) principal;
Object ctx = session.getAttribute("SPRING_SECURITY_CONTEXT");
if (!(ctx instanceof SecurityContext securityContext)) {
return null;
}
return user;
Authentication authentication = securityContext.getAuthentication();
if (authentication == null || !(authentication.getPrincipal() instanceof UserInfo userInfo)) {
return null;
}
return userInfo;
}
}
@@ -12,7 +12,6 @@ import com.yaoyuan.jiscuss.service.IDiscussionsTagsService;
import com.yaoyuan.jiscuss.service.IPostsService;
import com.yaoyuan.jiscuss.service.ITagsService;
import com.yaoyuan.jiscuss.service.IUsersService;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
@@ -29,7 +28,9 @@ import org.springframework.web.context.request.ServletRequestAttributes;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author yaoyuan2.chu
@@ -108,9 +109,9 @@ public class UserPostController extends BaseController {
* @param discussion
* @return
*/
@PostMapping(value = "/newDiscussions")
@PostMapping(value = {"/newDiscussions", "/newdiscussions"})
@ResponseBody
public String newDiscussions(@RequestBody DiscussionCustom discussion) {
public Map<String, Object> newDiscussions(@RequestBody DiscussionCustom discussion) {
logger.info(">>> newPost" + discussion);
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
@@ -141,13 +142,11 @@ public class UserPostController extends BaseController {
}
}
JSONObject resultobj = new JSONObject();
Map<String, Object> resultobj = new HashMap<>();
logger.info(">>>{}", saveDiscussion);
resultobj.put("msg", "添加主题成功");
resultobj.put("flag", true);
return resultobj.toString(); //
return resultobj;
}
@@ -170,7 +169,7 @@ public class UserPostController extends BaseController {
*/
@PostMapping(value = "/newPost")
@ResponseBody
public String newPosts(@RequestBody Post post) {
public Map<String, Object> newPosts(@RequestBody Post post) {
logger.info(">>> newpost" + post);
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
@@ -187,12 +186,11 @@ public class UserPostController extends BaseController {
}
Post savePost = postsService.insert(post);
JSONObject resultobj = new JSONObject();
Map<String, Object> resultobj = new HashMap<>();
logger.info(">>>{}", savePost);
resultobj.put("msg", "添加评论成功");
resultobj.put("flag", true);
return resultobj.toString(); //
return resultobj;
}
/**
@@ -208,7 +206,7 @@ public class UserPostController extends BaseController {
*/
@PostMapping(value = "/newtags")
@ResponseBody
public String newTags(@RequestBody Tag tag) {
public Map<String, Object> newTags(@RequestBody Tag tag) {
logger.info(">>> newTags" + tag);
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
@@ -220,12 +218,11 @@ public class UserPostController extends BaseController {
tag.setCreateTime(new Date());
Tag saveTag = tagsService.insert(tag);
JSONObject resultobj = new JSONObject();
Map<String, Object> resultobj = new HashMap<>();
logger.info(">>>{}", saveTag);
resultobj.put("msg", "添加标签成功");
resultobj.put("flag", true);
return resultobj.toString();
return resultobj;
}
/**
@@ -1,20 +1,28 @@
package com.yaoyuan.jiscuss.dto;
import com.yaoyuan.jiscuss.entity.User;
import org.springframework.stereotype.Component;
import org.mapstruct.Mapper;
import org.mapstruct.MappingConstants;
import org.mapstruct.ReportingPolicy;
/**
* Manual mapper between {@link User} entity and DTO objects.
* MapStruct mapper between {@link User} entity and its DTOs.
*
* <p>Intentionally excludes the {@code password} field from all output DTOs.
* Replace with a generated MapStruct mapper if mapping complexity grows.
* <p>The {@code password} field is intentionally never copied into a response and
* is left {@code null} when constructing a {@link User} from a create request —
* the caller (service layer) is responsible for BCrypt-hashing and setting it.
*/
@Component
public class UserMapper {
@Mapper(
componentModel = MappingConstants.ComponentModel.SPRING,
unmappedTargetPolicy = ReportingPolicy.IGNORE
)
public interface UserMapper {
/** Map a {@link User} entity to a safe {@link UserResponse} (no password). */
public UserResponse toResponse(User user) {
if (user == null) return null;
default UserResponse toResponse(User user) {
if (user == null) {
return null;
}
return new UserResponse(
user.getId(),
user.getUsername(),
@@ -32,13 +40,15 @@ public class UserMapper {
);
}
/** Map a {@link UserCreateRequest} to a new {@link User} entity (password left unset — caller must hash it). */
public User fromCreateRequest(UserCreateRequest request) {
/** Map a create request to a new {@link User} entity. Password must be set by the caller. */
default User fromCreateRequest(UserCreateRequest request) {
if (request == null) {
return null;
}
User user = new User();
user.setUsername(request.username());
user.setEmail(request.email());
user.setRealname(request.realname());
// Caller is responsible for BCrypt-hashing the password before calling usersService.insert()
return user;
}
}
@@ -1,63 +1,62 @@
package com.yaoyuan.jiscuss.handler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 处理无权请求
* Handles 403 access-denied responses.
*
* @author charlie
* <p>Behavior:
* <ul>
* <li>AJAX requests → JSON body with {@code code/msg}</li>
* <li>Regular page requests → simple HTML 403 response (no forward to /403,
* which previously caused a 404)</li>
* </ul>
*/
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
private Logger log = LoggerFactory.getLogger(CustomAccessDeniedHandler.class);
private static final Logger log = LoggerFactory.getLogger(CustomAccessDeniedHandler.class);
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
public void handle(HttpServletRequest request,
HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
boolean isAjax = ControllerTools.isAjaxRequest(request);
System.out.println("CustomAccessDeniedHandler handle");
if (!response.isCommitted()) {
if (isAjax) {
String msg = accessDeniedException.getMessage();
log.info("accessDeniedException.message:" + msg);
String accessDenyMsg = "{\"code\":\"403\",\"msg\":\"没有权限\"}";
ControllerTools.print(response, accessDenyMsg);
} else {
request.setAttribute(WebAttributes.ACCESS_DENIED_403, accessDeniedException);
response.setStatus(HttpStatus.FORBIDDEN.value());
RequestDispatcher dispatcher = request.getRequestDispatcher("/403");
dispatcher.forward(request, response);
if (response.isCommitted()) {
return;
}
log.warn("Access denied for {} → {}", request.getRequestURI(), accessDeniedException.getMessage());
response.setStatus(HttpStatus.FORBIDDEN.value());
response.setCharacterEncoding("UTF-8");
if (isAjaxRequest(request)) {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
try (PrintWriter writer = response.getWriter()) {
writer.write("{\"code\":403,\"msg\":\"没有权限\"}");
}
} else {
response.setContentType(MediaType.TEXT_HTML_VALUE);
try (PrintWriter writer = response.getWriter()) {
writer.write("<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><title>403 Forbidden</title></head>"
+ "<body><h1>403 Forbidden</h1><p>您没有权限访问该资源。</p></body></html>");
}
}
}
public static class ControllerTools {
public static boolean isAjaxRequest(HttpServletRequest request) {
return "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
}
public static void print(HttpServletResponse response, String msg) throws IOException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write(msg);
writer.flush();
writer.close();
}
private static boolean isAjaxRequest(HttpServletRequest request) {
return "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
}
}
@@ -69,7 +69,6 @@ public class PostsServiceImpl implements IPostsService {
}
//将回复添加到对应的位置
List list = Node.addAllNode(nodes, thenpostCustomList);
System.out.println();
//打印回复链表
Node.show(list);
@@ -49,9 +49,4 @@ public class DelTagsUtil {
return htmlStr;
}
public static void main(String[] args) {
String htmlStr = "test";
System.out.println(getTextFromHtml(htmlStr));
}
}