spring-boot blog system

Posted by stevietee on Mon, 08 Jun 2020 03:22:50 +0200

Spring-boot Personal Blog System

GitHub: https://github.com/Werdio66/myblog

1. Environmental Construction

gradle dependencies:

	implementation 'org.springframework.boot:spring-boot-starter-data-redis'
    implementation 'org.springframework.boot:spring-boot-starter-jdbc'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-aop'

    compile group: 'com.github.pagehelper', name: 'pagehelper-spring-boot-starter', version: '1.2.13'

    compile group: 'com.atlassian.commonmark', name: 'commonmark', version: '0.14.0'
    compile group: 'com.atlassian.commonmark', name: 'commonmark-ext-gfm-tables', version: '0.14.0'
    compile group: 'com.atlassian.commonmark', name: 'commonmark-ext-heading-anchor', version: '0.14.0'


    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.1'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    runtimeOnly 'mysql:mysql-connector-java'
    annotationProcessor 'org.projectlombok:lombok'

2. Database Design

3. Effect Display

Front End Interface

Home page: blog page breaks, categories, label displays, latest recommendations, search

Categories: Show all categories and the number of blogs in the current category, and show blogs in each category by page

Tags: Show all the tags and the number of blogs under the current tag, and show each tag's blog page by page

Archives: By Time

About me: Read personal information from profile

Background interface

Blog management

  • Paging shows blog information, which can be queried by title or category

  • Blog publishing, with Markdown editor integrated, allows you to choose from original or reprinted, categorized, tagged, etc.

Classification management: Paging display of classifications, adding classifications, deleting classifications and batch deleting classifications

Label management: Label paging, label addition, deletion and bulk deletion are not implemented, similar to classification management

4. Functional Realization

1. Logon Interception

Login interception for requests under /admin/** path, background administration page requires privileges

/**
 *  Logon Request Interception
 */
@Slf4j
public class LoginFilter implements Filter {

    private String noAuthUrl = "/admin";

    private Set<String> exclusionUrlSet = new ConcurrentSkipListSet<>();

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String url = filterConfig.getInitParameter("loginUrl");

        exclusionUrlSet.add(noAuthUrl);
        exclusionUrlSet.add("/login");
        exclusionUrlSet.add("redirect:/main.html");
        exclusionUrlSet.add("redirect:/admin");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        String uri = req.getRequestURI();
        log.info("[LongFilter]Current Request uri = {}", uri);

        // Intercept only resources under/admin/**
        if (!uri.startsWith(noAuthUrl)){
            chain.doFilter(request, response);
            return;
        }
		// URLs you add yourself can be configured into yml files
        if (exclusionUrlSet.contains(uri)){
            chain.doFilter(request, response);
            return;
        }

        String username = (String) req.getSession().getAttribute("loginUser");
        log.info("session Users in:{}", username);

        if (StringUtils.isEmpty(username)){
            log.error("You do not have access, please log in first");
            resp.sendRedirect("/admin");
            return;
        }

        // Login Successful
        chain.doFilter(request, response);
    }
}
2. Logging

Record the ip, uri, method name of the request before each request in the controller layer

@Slf4j
@Aspect
@Component
public class LogAspect {

    @Pointcut("execution(* com._520.myblog.controller.*.*(..))")
    public void log(){}

    @Before(value = "log()")
    public void before(JoinPoint joinPoint){
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
        String uri = request.getRequestURL().toString();
        String ip = request.getRemoteAddr();
        String method = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        RequestLog requestLog = new RequestLog(uri, ip, method, args);

        log.info("{}", requestLog);
    }

    @AfterReturning(returning = "result", pointcut = "log()")
    public void afterReturn(Object result){
        log.info("Page returned:{}", result);
    }


    @AllArgsConstructor
    private static class RequestLog{
        private final String url;
        private final String ip;
        private final String reqMethod;
        private final Object[] args;

        @Override
        public String toString() {
            return "RequestLog{" +
                    "url='" + url + '\'' +
                    ", ip='" + ip + '\'' +
                    ", reqMethod='" + reqMethod + '\'' +
                    ", args=" + Arrays.toString(args) +
                    '}';
        }
    }
}

3. Comment Management
   @Override
    public List<Comment> queryAllByBlogId(Long id) {

        // Query all comments for the specified blog
        List<Comment> allComments = commentMapper.queryAllByBlogId(id);

        // Store parent comments
        List<Comment> parentComments = new ArrayList<>();

        Map<Long, Comment> map = new HashMap<>();

        Map<Long, Comment> allComms = new HashMap<>();

        // Find Parent Comments
        for (Comment comment : allComments){
            if (comment.getParentId() == -1){
                parentComments.add(comment);
                // Save parent comments in map to set up child comments
                map.put(comment.getId(), comment);
            }

            allComms.put(comment.getId(), comment);
        }

        // Set Subcomments
        for (Comment comment : allComments){

            if (comment.getParentId() != -1){
                // Take out the parent comment of the current comment
                Comment parent = map.get(comment.getParentId());
                // Parent comments may not be the root node, so take them from all comments
                if (parent == null){
                    parent = allComms.get(comment.getParentId());
                }
                // Add the current comment to the child comment of the parent comment
                 parent.getChildComments().add(comment);
                // Set parent comment for current comment
                comment.setParentName(parent.getNickName());
            }
        }

        for (int i = 0; i < parentComments.size(); i++) {
            merge(parentComments.get(i));
        }

//        parentComments.forEach(System.out::println);
        return parentComments;
    }

    private void merge(Comment parentComment) {
        if (parentComment.getChildComments().isEmpty()){
            return;
        }

        List<Comment> childComments = parentComment.getChildComments();
        for (int i = 0; i < childComments.size(); i++) {
            merge(childComments.get(i));
            parentComment.getChildComments().addAll(childComments.get(i).getChildComments());
        }
    }

5. Optimization

1. You can store the front-end display blog in redis to increase speed
2. Use ab for pressure testing

Pressure the blog home page

Stress test the blog details page

Topics: Spring github Redis Mybatis