承接上文:基于springboot + vue 的个人博客搭建过程
接口url:/comments/article/{id}
请求方式:GET
请求参数:
参数名称 | 参数类型 | 说明 |
---|---|---|
id | long | 文章id(路径参数) |
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": [
{
"id": 53,
"author": {
"nickname": "李四",
"avatar": "http://localhost:8080/static/img/logo.b3a48c0.png",
"id": 1
},
"content": "写的好",
"childrens": [
{
"id": 54,
"author": {
"nickname": "李四",
"avatar": "http://localhost:8080/static/img/logo.b3a48c0.png",
"id": 1
},
"content": "111",
"childrens": [],
"createDate": "1973-11-26 08:52",
"level": 2,
"toUser": {
"nickname": "李四",
"avatar": "http://localhost:8080/static/img/logo.b3a48c0.png",
"id": 1
}
}
],
"createDate": "1973-11-27 09:53",
"level": 1,
"toUser": null
}
]
}
package com.raxcl.blog.controller;
import com.raxcl.blog.service.CommentsService;
import com.raxcl.blog.vo.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("comments")
public class CommentsController {
private final CommentsService commentsService;
public CommentsController(CommentsService commentsService){
this.commentsService = commentsService;
}
@GetMapping("article/{id}")
public Result comments(@PathVariable("id") Long articleId){
return commentsService.commentsByArticleId(articleId);
}
}
package com.raxcl.blog.service;
import com.raxcl.blog.vo.Result;
public interface CommentsService {
Result commentsByArticleId(Long articleId);
}
package com.raxcl.blog.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.raxcl.blog.dao.mapper.CommentMapper;
import com.raxcl.blog.dao.pojo.Comment;
import com.raxcl.blog.service.CommentsService;
import com.raxcl.blog.service.SysUserService;
import com.raxcl.blog.vo.CommentVo;
import com.raxcl.blog.vo.Result;
import com.raxcl.blog.vo.UserVo;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class CommentsServiceImpl implements CommentsService {
private final CommentMapper commentMapper;
private final SysUserService sysUserService;
public CommentsServiceImpl(CommentMapper commentMapper, SysUserService sysUserService) {
this.commentMapper = commentMapper;
this.sysUserService = sysUserService;
}
@Override
public Result commentsByArticleId(Long articleId) {
LambdaQueryWrapper<Comment> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Comment::getArticleId,articleId);
queryWrapper.eq(Comment::getLevel,1);
List<Comment> comments = commentMapper.selectList(queryWrapper);
return Result.success(copyList(comments));
}
private List<CommentVo> copyList(List<Comment> commentList) {
List<CommentVo> commentVoList = new ArrayList<>();
for (Comment comment : commentList){
commentVoList.add(copy(comment));
}
return commentVoList;
}
private CommentVo copy(Comment comment) {
CommentVo commentVo = new CommentVo();
BeanUtils.copyProperties(comment,commentVo);
//时间格式化
commentVo.setCreateDate(new DateTime(comment.getCreateDate()).toString("yyyy-MM-dd HH:mm"));
Long authorId = comment.getAuthorId();
UserVo userVo = sysUserService.findUserVoById(authorId);
commentVo.setAuthor(userVo);
//评论的评论
List<CommentVo> commentVoList = findCommentsByParentId(comment.getId());
commentVo.setChildrens(commentVoList);
if (comment.getLevel() >1){
Long toUid = comment.getToUid();
UserVo toUserVo = sysUserService.findUserVoById(toUid);
commentVo.setToUser(toUserVo);
}
return commentVo;
}
private List<CommentVo> findCommentsByParentId(Long id) {
LambdaQueryWrapper<Comment> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Comment::getParentId, id);
queryWrapper.eq(Comment::getLevel, 2);
List<Comment> comments = this.commentMapper.selectList(queryWrapper);
return copyList(comments);
}
}
package com.raxcl.blog.service;
import com.raxcl.blog.dao.pojo.SysUser;
import com.raxcl.blog.vo.Result;
import com.raxcl.blog.vo.UserVo;
public interface SysUserService {
UserVo findUserVoById(Long authorId);
}
package com.raxcl.blog.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.raxcl.blog.dao.mapper.SysUserMapper;
import com.raxcl.blog.dao.pojo.SysUser;
import com.raxcl.blog.service.SysUserService;
import com.raxcl.blog.utils.JWTUtils;
import com.raxcl.blog.vo.ErrorCode;
import com.raxcl.blog.vo.LoginUserVo;
import com.raxcl.blog.vo.Result;
import com.raxcl.blog.vo.UserVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.Objects;
@Service
public class SysUserServiceImpl implements SysUserService {
private final SysUserMapper sysUserMapper;
private final RedisTemplate<String, String> redisTemplate;
public SysUserServiceImpl(SysUserMapper sysUserMapper, RedisTemplate<String, String> redisTemplate) {
this.sysUserMapper = sysUserMapper;
this.redisTemplate = redisTemplate;
}
@Override
public UserVo findUserVoById(Long id) {
SysUser sysUser = sysUserMapper.selectById(id);
if(sysUser == null){
sysUser = new SysUser();
sysUser.setNickname("raxcl");
}
UserVo userVo = new UserVo();
BeanUtils.copyProperties(sysUser,userVo);
return userVo;
}
}
package com.raxcl.blog.dao.mapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.raxcl.blog.dao.pojo.Comment;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface CommentMapper extends BaseMapper<Comment> {
}
package com.raxcl.blog.dao.pojo;
import lombok.Data;
@Data
public class Comment {
private Long id;
private String content;
private Long createDate;
private Long articleId;
private Long authorId;
private Long parentId;
private Long toUid;
private Integer level;
}
package com.raxcl.blog.vo;
import lombok.Data;
import java.util.List;
@Data
public class CommentVo {
private Long id;
private UserVo author;
private String content;
private List<CommentVo> childrens;
private String createDate;
private Integer level;
private UserVo toUser;
}
package com.raxcl.blog.vo;
import lombok.Data;
@Data
public class UserVo {
private String nickname;
private String avatar;
private Long id;
}
接口url:/comments/create/change
请求方式:POST
请求参数:
参数名称 | 参数类型 | 说明 |
---|---|---|
articleId | long | 文章id |
content | string | 评论内容 |
parent | long | 父评论id |
toUserId | long | 被评论的用户id |
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": null
}
package com.raxcl.blog.config;
@Configuration
public class WebMVCConfig implements WebMvcConfigurer {
registry.addInterceptor(loginInterceptor).addPathPatterns("/test").addPathPatterns("/comments/create/change");
}
}
package com.raxcl.blog.controller;
import com.raxcl.blog.service.CommentsService;
import com.raxcl.blog.vo.Result;
import com.raxcl.blog.vo.param.CommentParam;
import org.springframework.web.bind.annotation.*;
import java.sql.ResultSet;
@RestController
@RequestMapping("comments")
public class CommentsController {
private final CommentsService commentsService;
public CommentsController(CommentsService commentsService){
this.commentsService = commentsService;
}
@PostMapping("create/change")
public Result comment(@RequestBody CommentParam commentParam){
return commentsService.comment(commentParam);
}
}
package com.raxcl.blog.service;
import com.raxcl.blog.vo.Result;
import com.raxcl.blog.vo.param.CommentParam;
public interface CommentsService {
Result comment(CommentParam commentParam);
}
package com.raxcl.blog.service.impl;
@Service
public class CommentsServiceImpl implements CommentsService {
@Override
public Result comment(CommentParam commentParam) {
SysUser sysUser = UserThreadLocal.get();
Comment comment = new Comment();
comment.setArticleId(commentParam.getArticleId());
comment.setAuthorId(sysUser.getId());
comment.setContent(commentParam.getContent());
comment.setCreateDate(System.currentTimeMillis());
Long parent = commentParam.getParent();
if (parent == null || parent == 0){
comment.setLevel(1);
}else {
comment.setLevel(2);
}
comment.setParentId(parent == null ? 0 : parent);
Long toUserId = commentParam.getToUserId();
comment.setToUid(toUserId == null ?0 : toUserId);
this.commentMapper.insert(comment);
return Result.success(null);
}
}
package com.raxcl.blog.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import java.util.List;
@Data
public class CommentVo {
//防止前端精度损失,把id转为string
//分布式id比较长,传到前端会有精度损失,必须转为string类型进行传输,就不会有问题
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
private UserVo author;
private String content;
private List<CommentVo> childrens;
private String createDate;
private Integer level;
private UserVo toUser;
}
package com.raxcl.blog.vo.param;
import lombok.Data;
@Data
public class CommentParam {
private Long articleId;
private String content;
private Long parent;
private Long toUserId;
}
接口url:/categorys
请求方式:GET
请求参数:
参数名称 | 参数类型 | 说明 |
---|---|---|
返回数据:
{
"success":true,
"code":200,
"msg":"success",
"data":
[
{"id":1,"avatar":"/category/front.png","categoryName":"前端"},
{"id":2,"avatar":"/category/back.png","categoryName":"后端"},
{"id":3,"avatar":"/category/lift.jpg","categoryName":"生活"},
{"id":4,"avatar":"/category/database.png","categoryName":"数据库"},
{"id":5,"avatar":"/category/language.png","categoryName":"编程语言"}
]
}
package com.raxcl.blog.controller;
import com.raxcl.blog.service.CategoryService;
import com.raxcl.blog.vo.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("categorys")
public class CategoryController {
private final CategoryService categoryService;
public CategoryController(CategoryService categoryService) {
this.categoryService = categoryService;
}
@GetMapping
public Result listCategory(){
return categoryService.findAll();
}
}
package com.raxcl.blog.service;
import com.raxcl.blog.vo.CategoryVo;
import com.raxcl.blog.vo.Result;
import java.util.List;
public interface CategoryService {
Result findAll();
}
package com.raxcl.blog.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.raxcl.blog.dao.mapper.CategoryMapper;
import com.raxcl.blog.dao.pojo.Category;
import com.raxcl.blog.service.CategoryService;
import com.raxcl.blog.vo.CategoryVo;
import com.raxcl.blog.vo.Result;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class CategoryServiceImpl implements CategoryService {
private final CategoryMapper categoryMapper;
public CategoryServiceImpl(CategoryMapper categoryMapper) {
this.categoryMapper = categoryMapper;
}
@Override
public Result findAll() {
List<Category> categories = categoryMapper.selectList(new LambdaQueryWrapper<>());
return Result.success(copyList(categories));
}
private Object copyList(List<Category> categoryList) {
List<CategoryVo> categoryVoList = new ArrayList<>();
for(Category category : categoryList){
categoryVoList.add(copy(category));
}
return categoryVoList;
}
private CategoryVo copy(Category category) {
CategoryVo categoryVo = new CategoryVo();
BeanUtils.copyProperties(category, categoryVo);
return categoryVo;
}
}
接口url:/tags
请求方式:GET
请求参数:
参数名称 | 参数类型 | 说明 |
---|---|---|
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": [
{
"id": 5,
"tagName": "springboot"
},
{
"id": 6,
"tagName": "spring"
},
{
"id": 7,
"tagName": "springmvc"
},
{
"id": 8,
"tagName": "11"
}
]
}
package com.raxcl.blog.controller;
import com.raxcl.blog.service.TagService;
import com.raxcl.blog.vo.Result;
import com.raxcl.blog.vo.TagVo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("tags")
public class TagsController {
@GetMapping
public Result findAll(){
return tagService.findAll();
}
}
package com.raxcl.blog.service;
import com.raxcl.blog.vo.Result;
import com.raxcl.blog.vo.TagVo;
import java.util.List;
public interface TagService {
Result findAll();
}
package com.raxcl.blog.service.impl;
@Service
public class TagServiceImpl implements TagService {
@Override
public Result findAll() {
List<Tag> tags = this.tagMapper.selectList((new LambdaQueryWrapper<>()));
return Result.success(copyList(tags));
}
}
接口url:/articles/publish
请求方式:POST
请求参数:
参数名称 | 参数类型 | 说明 |
---|---|---|
title | string | 文章标题 |
id | long | 文章id(编辑有值) |
body | object({content: “ww”, contentHtml: “ ww ↵”}) |
文章内容 |
category | {id: 2, avatar: “/category/back.png”, categoryName: “后端”} | 文章类别 |
summary | string | 文章概述 |
tags | [{id: 5}, {id: 6}] | 文章标签 |
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": {"id":12232323}
}
package com.raxcl.blog.controller;
import com.raxcl.blog.dao.pojo.Article;
import com.raxcl.blog.service.ArticleService;
import com.raxcl.blog.vo.ArticleVo;
import com.raxcl.blog.vo.param.ArticleParam;
import com.raxcl.blog.vo.param.PageParams;
import com.raxcl.blog.vo.Result;
import org.springframework.web.bind.annotation.*;
import java.sql.ResultSet;
@RestController
@RequestMapping("articles")
public class ArticleController {
private final ArticleService articleService;
public ArticleController(ArticleService articleService) {
this.articleService = articleService;
}
@PostMapping("publish")
public Result publish(@RequestBody ArticleParam articleParam){
return articleService.publish(articleParam);
}
}
package com.raxcl.blog.service;
import com.raxcl.blog.vo.ArticleVo;
import com.raxcl.blog.vo.param.ArticleParam;
import com.raxcl.blog.vo.param.PageParams;
import com.raxcl.blog.vo.Result;
public interface ArticleService {
/**
* 文章发布
* @param articleParam
* @return
*/
Result publish(ArticleParam articleParam);
}
package com.raxcl.blog.service.impl;
@Service
public class ArticleServiceImpl implements ArticleService {
@Override
@Transactional
public Result publish(ArticleParam articleParam) {
//此接口要加入到登录拦截当中
SysUser sysUser = UserThreadLocal.get();
/**
* 1. 发布文章 目的 构建Article对象
* 2. 作者id 当前的登录用户
* 3. 标签 要将标签加入到 关联列表当中
*/
Article article = new Article();
article.setAuthorId(sysUser.getId());
article.setCategoryId(articleParam.getCategory().getId());
article.setCreateDate(System.currentTimeMillis());
article.setCommentCounts(0);
article.setSummary(articleParam.getTitle());
article.setTitle(articleParam.getTitle());
article.setWeight(Article.Article_Common);
article.setBodyId(-1L);
//插入之后会生成一个文章id
this.articleMapper.insert(article);
//tags
List<TagVo> tags = articleParam.getTags();
if (tags != null) {
for (TagVo tag : tags) {
ArticleTag articleTag = new ArticleTag();
articleTag.setArticleId(article.getId());
articleTag.setTagId(tag.getId());
this.articleTagMapper.insert(articleTag);
}
}
//body
ArticleBody articleBody = new ArticleBody();
articleBody.setContent(articleParam.getBody().getContent());
articleBody.setArticleId(article.getId());
articleBodyMapper.insert(articleBody);
article.setBodyId(articleBody.getId());
articleMapper.updateById(article);
ArticleVo articleVo = new ArticleVo();
articleVo.setId(article.getId());
return Result.success(articleVo);
}
}
package com.raxcl.blog.controller;
import com.raxcl.blog.common.aop.LogAnnotation;
@RestController
@RequestMapping("articles")
public class ArticleController {
/**
* 首页 文章列表
* @param pageParams
* @return
*/
@PostMapping
//加上此注解 代表要对此接口记录日志
@LogAnnotation(module = "文章" , operator = "获取文章列表")
public Result listArticle(@RequestBody PageParams pageParams){
return articleService.listArticle(pageParams);
}
}
package com.raxcl.blog.common.aop;
import java.lang.annotation.*;
//Type 代表可以放在类上面,method代表可以放在方法上面
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {
//模块
String module() default "";
//方法
String operator() default "";
}
package com.raxcl.blog.common.aop;
import com.alibaba.fastjson.JSON;
import com.raxcl.blog.utils.HttpContextUtils;
import com.raxcl.blog.utils.IpUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
@Component
@Aspect //切面 定义了通知和切点的关系
@Slf4j
public class LogAspect {
@Pointcut("@annotation(com.raxcl.blog.common.aop.LogAnnotation)")
public void pt(){}
//环绕通知
@Around(("pt()"))
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
long beginTime = System.currentTimeMillis();
//执行方法
Object result = joinPoint.proceed();
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
//保存日志
recordLog(joinPoint, time);
return result;
}
private void recordLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
log.info("==========================log start====================");
log.info("module:{}", logAnnotation.module());
log.info("operation:{}", logAnnotation.operator());
//请求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
log.info("request method:{}", className +"."+ methodName + "()");
//请求的参数
Object[] args = joinPoint.getArgs();
String params = JSON.toJSONString(args[0]);
log.info("params:{}",params);
//获取request 设置IP地址
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
log.info("ip:{}", IpUtils.getIpAddr(request));
log.info("execute time : {} ms", time);
log.info("==========================log end=======================");
}
}
package com.raxcl.blog.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
/**
* 获取Ip
*/
@Slf4j
public class IpUtils {
/**
* 获取ip地址
*
* 使用Nginx等反向代理软件,则不能通过request.getRemoteAddr()获取ip地址
* 如果使用了多级代理的话,X-Forwarded-For的值并不止一个,而是一串ip地址,X-Forwarded-For中第一个非unknown的有效ip字符串,则
*/
public static String getIpAddr(HttpServletRequest request) {
String ip = null, unknown = "unknown", seperator = ",";
int maxLength = 15;
try {
ip = request.getHeader("x-forwarded-for");
if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARD_FOR");
}
if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
}catch (Exception e){
log.error("IpUtils ERROR",e);
}
//使用代理,则获取第一个IP地址
if (StringUtils.isEmpty(ip) && ip.length() > maxLength) {
int idx = ip.indexOf(seperator);
if (idx > 0){
ip = ip.substring(0, idx);
}
}
return ip;
}
/**
* 获取ip地址
*/
}
package com.raxcl.blog.utils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
public class HttpContextUtils {
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
}
接口url:/upload
请求方式:POST
请求参数:
参数名称 | 参数类型 | 说明 |
---|---|---|
image | file | 上传的文件名称 |
返回数据:
{
"success":true,
"code":200,
"msg":"success",
"data":"https://static.mszlu.com/aa.png"
}
<dependency>
<groupId>com.qiniugroupId>
<artifactId>qiniu-java-sdkartifactId>
<version>[7.7.0, 7.7.99]version>
dependency>
<!--图片上传服务器-->
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>[7.7.0, 7.7.99]</version>
</dependency>
#server
server.port=8888
spring.application.name=raxcl
# datasource
spring.datasource.url=jdbc:mysql://106.54.170.191:3306/blog?useUnicode=true&characterEncoding=UTF-8&serverTimeZone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#mybatis-plus
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.global-config.db-config.table-prefix=ms_
spring.redis.host=localhost
spring.redis.port=6379
# 上传文件总的最大值
spring.servlet.multipart.max-request-size=20MB
#单个文件的最大值
spring.servlet.multipart.max-file-size=2MB
package com.raxcl.blog.utils;
import com.alibaba.fastjson.JSON;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
@Component
public class QiniuUtils {
public static final String url = "r2aun4sy6.hn-bkt.clouddn.com";
public boolean upload(MultipartFile file, String fileName) {
//构造一个带指定 Region 对象的配置类
Configuration cfg = new Configuration(Region.region2());
//...其他参数参考类注释
UploadManager uploadManager = new UploadManager(cfg);
//...生成上传凭证,然后准备上传
String accessKey = "jJtOs_dPIScrZVN_agbTogFaJWIF58Ta-moo_sWA";
String secretKey = "YI7u3S2xUWthmtfbsGa0lKuwWT0CfOh-Zy0Lsbi9";
String bucket = "raxcl-blog";
//默认不指定key的情况下,以文件内容的hash值作为文件名
try {
byte[] uploadBytes = file.getBytes();
Auth auth = Auth.create(accessKey, secretKey);
String upToken = auth.uploadToken(bucket);
Response response = uploadManager.put(uploadBytes, fileName, upToken);
//解析上传成功的结果
DefaultPutRet putRet = JSON.parseObject(response.bodyString(), DefaultPutRet.class);
return true;
} catch (Exception ex) {
ex.printStackTrace();
}
return false;
}
}
package com.raxcl.blog.controller;
import com.raxcl.blog.utils.QiniuUtils;
import com.raxcl.blog.vo.Result;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.util.UUID;
@RestController
@RequestMapping("upload")
public class UploadController {
private final QiniuUtils qiniuUtils;
public UploadController(QiniuUtils qiniuUtils) {
this.qiniuUtils = qiniuUtils;
}
@PostMapping
public Result upload(@RequestParam("image")MultipartFile file){
//原始文件名称 比如 aa.png
String originalFilename = file.getOriginalFilename();
//唯一的文件名称
String fileName = UUID.randomUUID().toString() + "." + StringUtils.substringAfterLast(originalFilename,".");
//上传文件,上传到哪儿呢? 七牛云 云服务器 按量付费 速度快 把图片发放到离用户最近的服务器上
// 降低我们自身应用服务器的带宽消耗
boolean upload = qiniuUtils.upload(file, fileName);
if (upload){
return Result.success(QiniuUtils.url+ fileName);
}
return Result.fail(20001, "上传失败");
}
}
接口url:/categorys/detail
请求方式:GET
请求参数:
参数名称 | 参数类型 | 说明 |
---|---|---|
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": [
{
"id": 1,
"avatar": "/static/category/front.png",
"categoryName": "前端",
"description": "前端是什么,大前端"
},
{
"id": 2,
"avatar": "/static/category/back.png",
"categoryName": "后端",
"description": "后端最牛叉"
},
{
"id": 3,
"avatar": "/static/category/lift.jpg",
"categoryName": "生活",
"description": "生活趣事"
},
{
"id": 4,
"avatar": "/static/category/database.png",
"categoryName": "数据库",
"description": "没数据库,啥也不管用"
},
{
"id": 5,
"avatar": "/static/category/language.png",
"categoryName": "编程语言",
"description": "好多语言,该学哪个?"
}
]
}
package com.mszlu.blog.vo;
import lombok.Data;
@Data
public class CategoryVo {
private Long id;
private String avatar;
private String categoryName;
private String description;
}
package com.raxcl.blog.controller;
@RestController
@RequestMapping("categorys")
public class CategoryController {
private final CategoryService categoryService;
public CategoryController(CategoryService categoryService) {
this.categoryService = categoryService;
}
@GetMapping("/detail")
public Result categoriesDetail() {
return categoryService.findAllDetail();
}
}
package com.raxcl.blog.service;
public interface CategoryService {
Result findAllDetail();
}
package com.raxcl.blog.service.impl;
@Service
public class CategoryServiceImpl implements CategoryService {
@Override
public Result findAll() {
LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.select(Category::getId, Category::getCategoryName);
List<Category> categories = categoryMapper.selectList(queryWrapper);
return Result.success(copyList(categories));
}
@Override
public Result findAllDetail() {
LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
List<Category> categories = categoryMapper.selectList(queryWrapper);
//页面交互的对象
return Result.success(copyList(categories));
}
}
接口url:/tags/detail
请求方式:GET
请求参数:
参数名称 | 参数类型 | 说明 |
---|---|---|
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": [
{
"id": 5,
"tagName": "springboot",
"avatar": "/static/tag/java.png"
},
{
"id": 6,
"tagName": "spring",
"avatar": "/static/tag/java.png"
},
{
"id": 7,
"tagName": "springmvc",
"avatar": "/static/tag/java.png"
},
{
"id": 8,
"tagName": "11",
"avatar": "/static/tag/css.png"
}
]
}
package com.raxcl.blog.vo;
import lombok.Data;
@Data
public class TagVo {
private Long id;
private String tagName;
private String avatar;
}
package com.raxcl.blog.controller;
import com.raxcl.blog.service.TagService;
import com.raxcl.blog.vo.Result;
import com.raxcl.blog.vo.TagVo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("tags")
public class TagsController {
@GetMapping("/detail")
public Result findAllDetail(){
return tagService.findAllDetail();
}
}
package com.raxcl.blog.service;
import com.raxcl.blog.vo.Result;
import com.raxcl.blog.vo.TagVo;
import java.util.List;
public interface TagService {
Result findAllDetail();
}
package com.raxcl.blog.service.impl;
@Service
public class TagServiceImpl implements TagService {
@Override
public Result findAll() {
LambdaQueryWrapper<Tag> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.select(Tag::getId, Tag::getTagName);
List<Tag> tags = this.tagMapper.selectList(queryWrapper);
return Result.success(copyList(tags));
}
@Override
public Result findAllDetail() {
LambdaQueryWrapper<Tag> queryWrapper = new LambdaQueryWrapper<>();
List<Tag> tags = this.tagMapper.selectList(queryWrapper);
return Result.success(copyList(tags));
}
}
接口url:/category/detail/{id}
请求方式:GET
请求参数:
参数名称 | 参数类型 | 说明 |
---|---|---|
id | 分类id | 路径参数 |
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data":
{
"id": 1,
"avatar": "/static/category/front.png",
"categoryName": "前端",
"description": "前端是什么,大前端"
}
}
package com.raxcl.blog.controller;
import com.raxcl.blog.service.CategoryService;
import com.raxcl.blog.vo.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("categorys")
public class CategoryController {
@GetMapping("detail/{id}")
public Result categoriesDetailById(@PathVariable("id") Long id){
return categoryService.categoriesDetailById(id);
}
}
package com.raxcl.blog.vo.param;
import lombok.Data;
@Data
public class PageParams {
private int page = 1;
private int pageSize = 10;
private Long categoryId;
private Long tagId;
}
package com.raxcl.blog.service;
import com.raxcl.blog.vo.CategoryVo;
import com.raxcl.blog.vo.Result;
import java.util.List;
public interface CategoryService {
Result categoriesDetailById(Long id);
}
package com.raxcl.blog.service.impl;
@Service
public class CategoryServiceImpl implements CategoryService {
@Override
public Result categoriesDetailById(Long id) {
Category category = categoryMapper.selectById(id);
CategoryVo categoryVo = copy(category);
return Result.success(categoryVo);
}
package com.raxcl.blog.service.impl;
@Service
public class ArticleServiceImpl implements ArticleService {
@Override
public Result listArticle(PageParams pageParams) {
/**
* 1. 分页查询 article数据库表
*/
Page<Article> page = new Page<>(pageParams.getPage(), pageParams.getPageSize());
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
if (pageParams.getCategoryId() != null){
// and category_id=#{categoryId}
queryWrapper.eq(Article::getCategoryId, pageParams.getCategoryId());
}
//是否置顶进行排序
queryWrapper.orderByDesc(Article::getWeight, Article::getCreateDate);
Page<Article> articlePage = articleMapper.selectPage(page, queryWrapper);
//能直接返回吗? 很明显不能
List<Article> records = articlePage.getRecords();
List<ArticleVo> articleVoList = copyList(records, true, true);
return Result.success(articleVoList);
}
接口url:/tags/detail/{id}
请求方式:GET
请求参数:
参数名称 | 参数类型 | 说明 |
---|---|---|
id | 标签id | 路径参数 |
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data":
{
"id": 5,
"tagName": "springboot",
"avatar": "/static/tag/java.png"
}
}
@GetMapping("detail/{id}")
public Result findDetailById(@PathVariable("id") Long id){
return tagService.findDetailById(id);
}
Result findDetailById(Long id);
@Override
public Result findDetailById(Long id) {
Tag tag = tagMapper.selectById(id);
TagVo copy = copy(tag);
return Result.success(copy);
}
package com.raxcl.blog.service.impl;
@Service
public class ArticleServiceImpl implements ArticleService {
@Override
public Result listArticle(PageParams pageParams) {
/**
* 1. 分页查询 article数据库表
*/
Page<Article> page = new Page<>(pageParams.getPage(), pageParams.getPageSize());
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
if (pageParams.getCategoryId() != null){
// and category_id=#{categoryId}
queryWrapper.eq(Article::getCategoryId, pageParams.getCategoryId());
}
List<Long> articleIdList = new ArrayList<>();
if (pageParams.getTagId() != null){
//加入标签 条件查询
//article表中 并没有tag字段 一篇文章 有多个标签
//article_tag article_id 1:n tag_id
LambdaQueryWrapper<ArticleTag> articleTagLambdaQueryWrapper = new LambdaQueryWrapper<>();
articleTagLambdaQueryWrapper.eq(ArticleTag::getTagId, pageParams.getTagId());
List<ArticleTag> articleTags = articleTagMapper.selectList(articleTagLambdaQueryWrapper);
for (ArticleTag articleTag : articleTags){
articleIdList.add(articleTag.getArticleId());
}
if (articleIdList.size() > 0){
//and id in (1,2,3)
queryWrapper.in(Article::getId, articleIdList);
}
}
//是否置顶进行排序
queryWrapper.orderByDesc(Article::getWeight, Article::getCreateDate);
Page<Article> articlePage = articleMapper.selectPage(page, queryWrapper);
//能直接返回吗? 很明显不能
List<Article> records = articlePage.getRecords();
List<ArticleVo> articleVoList = copyList(records, true, true);
return Result.success(articleVoList);
}
}
接口url:/articles
请求方式:POST
请求参数:
参数名称 | 参数类型 | 说明 |
---|---|---|
year | string | 年 |
month | string | 月 |
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": [文章列表,数据同之前的文章列表接口]
}
package com.raxcl.blog.vo.param;
import lombok.Data;
@Data
public class PageParams {
private int page = 1;
private int pageSize = 10;
private Long categoryId;
private Long tagId;
private String year;
private String month;
public String getMonth(){
if (this.month != null && this.month.length() == 1){
return "0" + this.month;
}
return this.month;
}
}
package com.raxcl.blog.service.impl;
@Service
public class ArticleServiceImpl implements ArticleService {
/*@Override
public Result listArticle(PageParams pageParams) {
*//**
* 1. 分页查询 article数据库表
*//*
Page<Article> page = new Page<>(pageParams.getPage(), pageParams.getPageSize());
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
if (pageParams.getCategoryId() != null){
// and category_id=#{categoryId}
queryWrapper.eq(Article::getCategoryId, pageParams.getCategoryId());
}
List<Long> articleIdList = new ArrayList<>();
if (pageParams.getTagId() != null){
//加入标签 条件查询
//article表中 并没有tag字段 一篇文章 有多个标签
//article_tag article_id 1:n tag_id
LambdaQueryWrapper<ArticleTag> articleTagLambdaQueryWrapper = new LambdaQueryWrapper<>();
articleTagLambdaQueryWrapper.eq(ArticleTag::getTagId, pageParams.getTagId());
List<ArticleTag> articleTags = articleTagMapper.selectList(articleTagLambdaQueryWrapper);
for (ArticleTag articleTag : articleTags){
articleIdList.add(articleTag.getArticleId());
}
if (articleIdList.size() > 0){
//and id in (1,2,3)
queryWrapper.in(Article::getId, articleIdList);
}
}
//是否置顶进行排序
queryWrapper.orderByDesc(Article::getWeight, Article::getCreateDate);
Page<Article> articlePage = articleMapper.selectPage(page, queryWrapper);
//能直接返回吗? 很明显不能
List<Article> records = articlePage.getRecords();
List<ArticleVo> articleVoList = copyList(records, true, true);
return Result.success(articleVoList);
}*/
@Override
public Result listArticle(PageParams pageParams) {
Page<Article> page = new Page<>(pageParams.getPage(), pageParams.getPageSize());
IPage<Article> articleIPage = this.articleMapper.listArchive(page,pageParams.getCategoryId(),
pageParams.getTagId(),pageParams.getYear(),pageParams.getMonth());
return Result.success(copyList(articleIPage.getRecords(),true,true));
}
package com.raxcl.blog.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.raxcl.blog.dao.dto.Archives;
import com.raxcl.blog.dao.pojo.Article;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface ArticleMapper extends BaseMapper<Article> {
IPage<Article> listArchive(Page<Article> page, Long categoryId, Long tagId, String year, String month);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!--MyBatis配置文件-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.raxcl.blog.dao.mapper.ArticleMapper">
<select id="listArchives" resultType="com.raxcl.blog.dao.dto.Archives">
select from_unixtime(create_date/1000,'%Y') year, from_unixtime(create_date/1000,'%m') month, count(*) count
from ms_article
group by year, month
</select>
<select id="listArchive" resultType="com.raxcl.blog.dao.pojo.Article">
select * from ms_article
<where>
<if test="categoryId != null">
and category_id = #{categoryId}
</if>
<if test="year != null and year.length>0 and month !=null and month.length>0">
and (from_unixtime(create_date/1000,'%Y') = #{year} and from_unixtime(create_date/1000,'%m') = #{month} )
</if>
<if test="tagId != null">
and id in (select article_id from ms_article_tag where tag_id=#{tagId})
</if>
</where>
order by create_date desc
</select>
</mapper>
内存的访问速度远远大于磁盘的访问速度(1000倍起)
package com.raxcl.blog.common.cache;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Cache {
long expire() default 60 * 1000;
String name() default "";
}
package com.raxcl.blog.common.cache;
import com.alibaba.fastjson.JSON;
import com.raxcl.blog.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.time.Duration;
@Aspect
@Component
@Slf4j
public class CacheAspect {
private final RedisTemplate<String, String> redisTemplate;
public CacheAspect(RedisTemplate<String, String> redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Pointcut("@annotation(com.raxcl.blog.common.cache.Cache)")
public void pt(){}
@Around("pt()")
public Object around(ProceedingJoinPoint pjp){
try {
Signature signature = pjp.getSignature();
//类名
String className = pjp.getTarget().getClass().getSimpleName();
//调用的方法名
String methodName = signature.getName();
Class[] paramterTypes = new Class[pjp.getArgs().length];
Object[] args = pjp.getArgs();
//参数
String params = "";
for(int i=0; i<args.length; i++){
if (args[i] != null) {
params += JSON.toJSONString(args[i]);
paramterTypes[i] = args[i].getClass();
}else {
paramterTypes[i] = null;
}
}
if (StringUtils.isNotEmpty(params)){
//加密 以防出现key过长以及字符转义获取不到的情况
params = DigestUtils.md5Hex(params);
}
Method method = pjp.getSignature().getDeclaringType().getMethod(methodName, paramterTypes);
//获取Cache注解
Cache annotation = method.getAnnotation(Cache.class);
//缓存过期时间
long expire = annotation.expire();
//缓存名称
String name = annotation.name();
//先从redis获取
String redisKey = name + "::" + className + "::" + methodName + "::" + params;
String redisValue = redisTemplate.opsForValue().get(redisKey);
if (StringUtils.isNotEmpty(redisValue)){
log.info("走了缓存~~~{},{}",className,methodName);
return JSON.parseObject(redisValue, Result.class);
}
Object proceed = pjp.proceed();
redisTemplate.opsForValue().set(redisKey,JSON.toJSONString(proceed), Duration.ofMillis(expire));
log.info("存入缓存~~~{},{}",className,methodName);
return proceed;
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return Result.fail(-999, "系统错误");
}
}
package com.raxcl.blog.controller;
@RestController
@RequestMapping("articles")
public class ArticleController {
/**
* 首页 文章列表
* @param pageParams
* @return
*/
@PostMapping
//加上此注解 代表要对此接口记录日志
@LogAnnotation(module = "文章" , operator = "获取文章列表")
@Cache(expire = 5 * 60 * 1000, name = "list_article")
public Result listArticle(@RequestBody PageParams pageParams){
return articleService.listArticle(pageParams);
}
/**
* 首页 最热文章
* @return
*/
@PostMapping("hot")
@Cache(expire = 5 * 60 * 1000, name = "hot_article")
public Result hotArticle(){
int limit = 5;
return articleService.hotArticle(limit);
}
/**
* 首页 最新文章
* @return
*/
@PostMapping("new")
@Cache(expire = 5 * 60 * 1000, name = "new_article")
public Result newArticle(){
int limit = 5;
return articleService.newArticle(limit);
}
}
```java
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>blog-parent</artifactId>
<groupId>com.raxcl</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>blog-admin</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<!-- 排除 默认使用的logback -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- log4j2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
</project>
server.port=8889
spring.application.name=mszlu_admin_blog
#数据库的配置
# datasource
spring.datasource.url=jdbc:mysql://106.54.170.191:3306/blog?useUnicode=true&characterEncoding=UTF-8&serverTimeZone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#mybatis-plus
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.global-config.db-config.table-prefix=ms_
package com.raxcl.blog.admin.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.raxcl.blog.admin.mapper")
public class MybatisPlusConfig {
//分页插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
package com.raxcl.blog.admin.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
@Data
public class Permission {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private String path;
private String description;
}
package com.raxcl.blog.admin.model.params;
import lombok.Data;
@Data
public class PageParam {
private Integer currentPage;
private Integer pageSize;
private String queryString;
}
package com.raxcl.blog.admin.vo;
import lombok.Data;
import java.util.List;
@Data
public class PageResult<T> {
private List<T> list;
private Long total;
}
package com.raxcl.blog.admin.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Result {
private boolean success;
private int code;
private String msg;
private Object data;
public static Result success(Object data) {
return new Result(true, 200, "success", data);
}
public static Result fail(int code, String msg) {
return new Result(false, code, msg, null);
}
}
package com.raxcl.blog.admin.controller;
import com.raxcl.blog.admin.model.params.PageParam;
import com.raxcl.blog.admin.pojo.Permission;
import com.raxcl.blog.admin.service.PermissionService;
import com.raxcl.blog.admin.vo.Result;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("admin")
public class AdminController {
private final PermissionService permissionService;
public AdminController(PermissionService permissionService) {
this.permissionService = permissionService;
}
@PostMapping("permission/permissionList")
public Result permissionList(@RequestBody PageParam pageParam){
return permissionService.listPermission(pageParam);
}
@PostMapping("permission/add")
public Result add(@RequestBody Permission permission){
return permissionService.add(permission);
}
@PostMapping("permission/update")
public Result update(@RequestBody Permission permission){
return permissionService.update(permission);
}
@GetMapping("permission/delete/{id}")
public Result delete(@PathVariable("id") Long id){
return permissionService.delete(id);
}
}
package com.raxcl.blog.admin.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.raxcl.blog.admin.mapper.PermissionMapper;
import com.raxcl.blog.admin.model.params.PageParam;
import com.raxcl.blog.admin.pojo.Permission;
import com.raxcl.blog.admin.vo.PageResult;
import com.raxcl.blog.admin.vo.Result;
import org.springframework.stereotype.Service;
@Service
public class PermissionService {
private final PermissionMapper permissionMapper;
public PermissionService(PermissionMapper permissionMapper) {
this.permissionMapper = permissionMapper;
}
public Result listPermission(PageParam pageParam){
/**
* 要的数据,管理台,表的所有的字段 Permission
* 分页查询
*/
Page<Permission> page = new Page<>(pageParam.getCurrentPage(),pageParam.getPageSize());
LambdaQueryWrapper<Permission> queryWrapper = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(pageParam.getQueryString())) {
queryWrapper.eq(Permission::getName,pageParam.getQueryString());
}
Page<Permission> permissionPage = this.permissionMapper.selectPage(page, queryWrapper);
PageResult<Permission> pageResult = new PageResult<>();
pageResult.setList(permissionPage.getRecords());
pageResult.setTotal(permissionPage.getTotal());
return Result.success(pageResult);
}
public Result add(Permission permission) {
this.permissionMapper.insert(permission);
return Result.success(null);
}
public Result update(Permission permission) {
this.permissionMapper.updateById(permission);
return Result.success(null);
}
public Result delete(Long id) {
this.permissionMapper.deleteById(id);
return Result.success(null);
}
}
package com.raxcl.blog.admin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.raxcl.blog.admin.pojo.Permission;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface PermissionMapper extends BaseMapper<Permission> {
}
package com.raxcl.blog.admin.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
public static void main(String[] args) {
//加密策略 MD5 不安全 彩虹表 MD5 加盐
String mszlu = new BCryptPasswordEncoder().encode("mszlu");
System.out.println(mszlu);
}
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() //开启登录认证
// .antMatchers("/user/findAll").hasRole("admin") //访问接口需要admin的角色
.antMatchers("/css/**").permitAll()
.antMatchers("/img/**").permitAll()
.antMatchers("/js/**").permitAll()
.antMatchers("/plugins/**").permitAll()
.antMatchers("/admin/**").access("@authService.auth(request,authentication)") //自定义service 来去实现实时的权限认证
.antMatchers("/pages/**").authenticated()
.and().formLogin()
.loginPage("/login.html") //自定义的登录页面
.loginProcessingUrl("/login") //登录处理接口
.usernameParameter("username") //定义登录时的用户名的key 默认为username
.passwordParameter("password") //定义登录时的密码key,默认是password
.defaultSuccessUrl("/pages/main.html")
.failureUrl("/login.html")
.permitAll() //通过 不拦截,更加前面配的路径决定,这是指和登录表单相关的接口 都通过
.and().logout() //退出登录配置
.logoutUrl("/logout") //退出登录接口
.logoutSuccessUrl("/login.html")
.permitAll() //退出登录的接口放行
.and()
.httpBasic()
.and()
.csrf().disable() //csrf关闭 如果自定义登录 需要关闭
.headers().frameOptions().sameOrigin();
}
}
package com.raxcl.blog.admin.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
@Data
public class Admin {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String password;
}
package com.raxcl.blog.admin.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.raxcl.blog.admin.mapper.AdminMapper;
import com.raxcl.blog.admin.mapper.PermissionMapper;
import com.raxcl.blog.admin.pojo.Admin;
import com.raxcl.blog.admin.pojo.Permission;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class AdminService {
private final AdminMapper adminMapper;
private final PermissionMapper permissionMapper;
public AdminService(AdminMapper adminMapper, PermissionMapper permissionMapper) {
this.adminMapper = adminMapper;
this.permissionMapper = permissionMapper;
}
public Admin findAdminByUserName(String username){
LambdaQueryWrapper<Admin> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Admin::getUsername, username).last("limit 1");
Admin adminUser = adminMapper.selectOne(queryWrapper);
return adminUser;
}
public List<Permission> findPermissionsByAdminId(Long adminId){
return permissionMapper.findPermissionsByAdminId(adminId);
}
}
package com.raxcl.blog.admin.service;
import com.raxcl.blog.admin.pojo.Admin;
import com.raxcl.blog.admin.pojo.Permission;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@Service
@Slf4j
public class AuthService {
private final AdminService adminService;
public AuthService(AdminService adminService) {
this.adminService = adminService;
}
public boolean auth(HttpServletRequest request, Authentication authentication){
String requestURI = request.getRequestURI();
log.info("request url:{}", requestURI);
//true代表放行 false 代表拦截
Object principal = authentication.getPrincipal();
if (principal == null || "annoymousUser".equals(principal)){
//未登录
return false;
}
UserDetails userDetails = (UserDetails) principal;
String username = userDetails.getUsername();
Admin admin = adminService.findAdminByUserName(username);
if (admin == null){
return false;
}
if (admin.getId() == 1){
//认为是超级管理员
return true;
}
List<Permission> permissions = adminService.findPermissionsByAdminId(admin.getId());
requestURI = StringUtils.split(requestURI, '?')[0];
for (Permission permission : permissions) {
if (requestURI.equals(permission.getPath())){
log.info("权限通过");
return true;
}
}
return false;
}
}
package com.raxcl.blog.admin.service;
import org.springframework.security.core.GrantedAuthority;
public class MySimpleGrantedAuthority implements GrantedAuthority {
private String authority;
private String path;
public MySimpleGrantedAuthority() {
}
public MySimpleGrantedAuthority(String authority) {
this.authority = authority;
}
public MySimpleGrantedAuthority(String authority, String path) {
this.authority = authority;
this.path = path;
}
@Override
public String getAuthority() {
return authority;
}
public String getPath(){
return path;
}
}
package com.raxcl.blog.admin.service;
import com.raxcl.blog.admin.pojo.Admin;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
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.Component;
import java.util.ArrayList;
@Component
@Slf4j
public class SecurityUserService implements UserDetailsService {
private final AdminService adminService;
public SecurityUserService(AdminService adminService) {
this.adminService = adminService;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("username:{}",username);
//当用户登录的时候,springSecurity就会将请求转发到此
//根据用户名查找用户,不存在则抛出异常, 存在则将用户名,密码,授权列表 组装成springSecurity的User对象并返回
Admin adminUser = adminService.findAdminByUserName(username);
if (adminUser == null){
throw new UsernameNotFoundException("用户名不存在");
}
ArrayList<GrantedAuthority> authorities = new ArrayList<>();
UserDetails userDetails = new User(username,adminUser.getPassword(),authorities);
//剩下的认证 就由框架帮我们完成
return userDetails;
}
}
package com.raxcl.blog.admin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.raxcl.blog.admin.pojo.Admin;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface AdminMapper extends BaseMapper<Admin> {
}
package com.raxcl.blog.admin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.raxcl.blog.admin.pojo.Permission;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface PermissionMapper extends BaseMapper<Permission> {
@Select("select * from ms_permission where id in (select permission_id from ms_admin_permission where admin_id=#{adminId})")
List<Permission> findPermissionsByAdminId(Long adminId);
}
待优化项: 添加角色,用户拥有多个角色,一个角色拥有多个权限
总结:
然后就是服务器购买,域名注册,备案,将项目部署上线
跳转: 基于springboot + vue 的个人博客搭建过程(上线)