Java后端接口和Vue结合element ui前台
1.先看效果
发表文章
后台登录
博客详情
Java后端接口开发:
使用技术 springboot+mybaitsPlus+shiro+jwt+lombok+redis;数据库mysql5.7+
这里使用IDEA来开发后台接口,新建步骤比较简单,略了
2.创建shpringboot项目
项目结构:
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
3。整合MyBtisPlus
1.引入相关依赖
<!--mp-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--mp代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.2.0</version>
</dependency>
2.编写配置文件
#端口
server:
port: 8081
#数据库相关
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/vueblog?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 123456
mybatis-plus:
type-aliases-package: com.hu.my_web_site.entity
mapper-locations: classpath*:/mapper/**.xml
3.编写GenerateCode自动生成代码(网上一大堆)
// 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
public class CodeGenerator {
/**
* <p>
* 读取控制台内容
* </p>
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotEmpty(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
// gc.setOutputDir("D:\\test");
gc.setAuthor("hucong");
gc.setOpen(false);
// gc.setSwagger2(true); 实体属性 Swagger2 注解
gc.setServiceName("%sService");
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/vueblog?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=UTC");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName(null);
pc.setParent("com.hu.my_web_site");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
// 如果模板引擎是 freemarker
String templatePath = "/templates/mapper.xml.ftl";
// 如果模板引擎是 velocity
// String templatePath = "/templates/mapper.xml.vm";
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return projectPath + "/src/main/resources/mapper/"
+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix("m_");
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
4.数据库相关sql,这里就两张表,登录注册表,存Blog文章表
DROP TABLE IF EXISTS `m_blog`;
CREATE TABLE `m_blog` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL,
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL,
`created` datetime(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0),
`status` tinyint(4) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 34 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
CREATE TABLE `m_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`email` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`password` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`status` int(5) NOT NULL,
`created` datetime(0) NULL DEFAULT NULL,
`last_login` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `UK_USERNAME`(`username`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `m_user` VALUES (1, 'hucong', '’http://www.16sucai.com/2016/05/82507.html‘', '1069997178@qq.com', '96e79218965eb72c92a549dd5a330112', 0, '2020-04-20 10:44:01', NULL);
建之后大概项目的结构如上图
测试接口(就是测与数据库是否连通)我就不写了,肯定对的!!!哈哈
mybatisplus整合完毕
4.统一结果封装
Result的类,这个用于异步统一返回的结果封装。一般来说,结果里面有几个要素必要的,(封装在common包下)
是否成功,可用code表示(如200表示成功,400表示异常)
结果消息
结果数据
@Data
public class Result implements Serializable {
private int code; // 200是正常,非200表示异常
private String msg;
private Object data;
public static Result succ(Object data) {
return succ(200, "操作成功", data);
}
public static Result succ(int code, String msg, Object data) {
Result r = new Result();
r.setCode(code);
r.setMsg(msg);
r.setData(data);
return r;
}
public static Result fail(String msg) {
return fail(400, msg, null);
}
public static Result fail(String msg, Object data) {
return fail(400, msg, data);
}
public static Result fail(int code, String msg, Object data) {
Result r = new Result();
r.setCode(code);
r.setMsg(msg);
r.setData(data);
return r;
}
}
5。整合shiro+jwt,实现会话共享
基本思路:
1**.导入pom依赖**
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis-spring-boot-starter</artifactId>
<version>3.2.1</version>
</dependency>
<!-- hutool工具类-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.3</version>
</dependency>
<!-- jwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2.编写yml
#shiro-redis
shiro-redis:
enabled: true
redis-manager:
host: 127.0.0.1:6379
3.编写配置类ShiroConfig(放在config包下)
@Configuration
public class ShiroConfig {
@Autowired
JwtFilter jwtFilter;
@Bean
public SessionManager sessionManager(RedisSessionDAO redisSessionDAO) {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
// inject redisSessionDAO
sessionManager.setSessionDAO(redisSessionDAO);
return sessionManager;
}
@Bean
public DefaultWebSecurityManager securityManager(AccountRealm accountRealm,
SessionManager sessionManager,
RedisCacheManager redisCacheManager) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(accountRealm);
//inject sessionManager
securityManager.setSessionManager(sessionManager);
// inject redisCacheManager
securityManager.setCacheManager(redisCacheManager);
return securityManager;
}
/**
* 定义过滤器链
* @return
*/
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/**", "jwt");
chainDefinition.addPathDefinitions(filterMap);
return chainDefinition;
}
@Bean("shiroFilterFactoryBean")
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager,
ShiroFilterChainDefinition shiroFilterChainDefinition) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
Map<String, Filter> filters = new HashMap<>();
filters.put("jwt", jwtFilter);
shiroFilter.setFilters(filters);
Map<String, String> filterMap = shiroFilterChainDefinition.getFilterChainMap();
shiroFilter.setFilterChainDefinitionMap(filterMap);
return shiroFilter;
}
}
1.引入RedisSessionDAO和RedisCacheManager,为了解决shiro的权限数据和会话信息能保存到redis中,实现会话共享。
2.重写了SessionManager和DefaultWebSecurityManager,同时在DefaultWebSecurityManager中为了关闭shiro自带的session方式,需要设置为false,这样用户就不再能通过session方式登录shiro。后面将采用jwt凭证登录。
3.在ShiroFilterChainDefinition中,不再通过编码形式拦截Controller访问路径,而是所有的路由都需要经过JwtFilter这个过滤器,然后判断请求头中是否含有jwt的信息,有就登录,没有就跳过。跳过之后,有Controller中的shiro注解进行再次拦截,比如@RequiresAuthentication,这样控制权限访问。那么,接下来,ShiroConfig中出现的AccountRealm(自定义Realm),还有JwtFilter(过滤器)。
4.AccountRealm(放在shiro包下)
AccountRealm是shiro进行登录或者权限校验的逻辑所在,算是核心了,
需要重写3个方法,分别是
supports:为了让realm支持jwt的凭证校验
doGetAuthorizationInfo:权限校验
doGetAuthenticationInfo:登录认证校验
@Component
public class AccountRealm extends AuthorizingRealm {
@Autowired
JwtUtils jwtUtils;
@Autowired
UserService userService;
/**
* 为了让realm支持jwt的凭证校验
* @param token
* @return
*/
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JwtToken;
}
/**
* 授权
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
/**
* 认证
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
JwtToken jwtToken = (JwtToken) token;
String userId = jwtUtils.getClaimByToken((String) jwtToken.getPrincipal()).getSubject();
User user = userService.getById(Long.valueOf(userId));
if (user == null) {
throw new UnknownAccountException("账户不存在");
}
if (user.getStatus() == -1) {
throw new LockedAccountException("账户已被锁定");
}
AccountProfile profile = new AccountProfile();
BeanUtil.copyProperties(user, profile);
return new SimpleAuthenticationInfo(profile, jwtToken.getCredentials(), getName());
}
}
其实主要就是doGetAuthenticationInfo登录认证这个方法,可以看到我们通过jwt获取到用户信息,判断用户的状态,最后异常就抛出对应的异常信息,否者封装成SimpleAuthenticationInfo返回给shiro。
接下来逐步分析里面出现的新类:1、shiro默认supports的是UsernamePasswordToken,而我们现在采用了jwt的方式,所以这里自定义一个JwtToken,来完成shiro的supports方法。
5.JwtToken(shiro包下)
1.jwttken:
public class JwtToken implements AuthenticationToken {
//shiro默认supports的是UsernamePasswordToken,
// 而我们现在采用了jwt的方式,所以这里我们自定义一个JwtToken,
// 来完成shiro的supports方法
private String token;
public JwtToken(String jwt) {
this.token = jwt;
}
@Override
public Object getPrincipal() {
return token;
}
@Override
public Object getCredentials() {
return token;
}
}
2、JwtUtils是个生成和校验jwt的工具类,其中有些jwt相关的密钥信息是从项目配置文件中配置的:(放在utils包下)
/**
* jwt工具类
*/
@Slf4j
@Data
@Component
@ConfigurationProperties(prefix = "hu.jwt")
public class JwtUtils {
private String secret;
private long expire;
private String header;
/**
* 生成jwt token
*/
public String generateToken(long userId) {
Date nowDate = new Date();
//过期时间
Date expireDate = new Date(nowDate.getTime() + expire * 1000);
return Jwts.builder()
.setHeaderParam("typ", "JWT")
.setSubject(userId+"")
.setIssuedAt(nowDate)
.setExpiration(expireDate)
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public Claims getClaimByToken(String token) {
try {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}catch (Exception e){
log.debug("validate is token error ", e);
return null;
}
}
/**
* token是否过期
* @return true:过期
*/
public boolean isTokenExpired(Date expiration) {
return expiration.before(new Date());
}
}
3、而在AccountRealm我们还用到了AccountProfile,这是为了登录成功之后返回的一个用户信息的载体,
@Data
public class AccountProfile implements Serializable {
//AccountProfile,这是为了登录成功之后返回的一个用户信息的载体,
private Long id;
private String username;
private String avatar;
private String email;
}
ok,基本的校验的路线完成之后,需要少量的基本信息配置:
hu:
jwt:
#
secret: f4e2e52034348f86b67cde581c0f9eb5
# token
expire: 604800
header: Authorization
6.JwtFilter(shiro包下)
这个过滤器是我们的重点,这里继承的是Shiro内置的AuthenticatingFilter,一个可以内置了可以自动登录方法的的过滤器,继承BasicHttpAuthenticationFilter也是可以的。
需要重写几个方法:
createToken:实现登录,需要生成我自定义支持的JwtTokenon
AccessDenied:拦截校验,当头部没有Authorization时候,直接通过,不需要自动登录;当带有的时候,首先校验jwt的有效性,没问题就直接执行executeLogin方法实现自动登录
onLoginFailure:登录异常时候进入的方法,直接把异常信息封装然后抛出
preHandle:拦截器的前置拦截,因为这是前后端分离项目,项目中除了需要跨域全局配置之外,在拦截器中也需要提供跨域支持。这样,拦截器才不会在进入Controller之前就被限制了。
@Component
public class JwtFilter extends AuthenticatingFilter {
@Autowired
JwtUtils jwtUtils;
//实现登录,我们需要生成我们自定义支持的JwtToken
@Override
protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String jwt = request.getHeader("Authorization");
if(StringUtils.isEmpty(jwt)) {
return null;
}
return new JwtToken(jwt);
}
/**
* 拦截校验,当头部没有Authorization时候,我们直接通过,不需要自动登录;
* 当带有的时候,首先我们校验jwt的有效性,没问题我们就直接执行executeLogin方法实现自动登录
* @param servletRequest
* @param servletResponse
* @return
* @throws Exception
*/
@Override
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String jwt = request.getHeader("Authorization");
if(StringUtils.isEmpty(jwt)) {
return true;
} else {
// 校验jwt
Claims claim = jwtUtils.getClaimByToken(jwt);
if(claim == null || jwtUtils.isTokenExpired(claim.getExpiration())) {
throw new ExpiredCredentialsException("token已失效,请重新登录");
}
// 执行登录
return executeLogin(servletRequest, servletResponse);
}
}
/**
* 登录异常时候进入的方法,我们直接把异常信息封装然后抛出
* @param token
* @param e
* @param request
* @param response
* @return
*/
@Override
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
Throwable throwable = e.getCause() == null ? e : e.getCause();
Result result = Result.fail(throwable.getMessage());
String json = JSONUtil.toJsonStr(result);
try {
httpServletResponse.getWriter().print(json);
} catch (IOException ioException) {
}
return false;
}
/**
* 拦截器的前置拦截,因为我们是前后端分析项目,项目中除了需要跨域全局配置之外,
* 我们再拦截器中也需要提供跨域支持。这样,拦截器才不会在进入Controller之前就被限制了。
* @param request
* @param response
* @return
* @throws Exception
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
// 跨域时会首先发送一个OPTIONS请求,这里我们给OPTIONS请求直接返回正常状态
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(org.springframework.http.HttpStatus.OK.value());
return false;
}
return super.preHandle(request, response);
}
}
shiro就已经完成整合进来了,并且使用了jwt进行身份校验。
6全局异常处理
位置:(package com.hu.my_web_site.common.exception;)
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* @ControllerAdvice表示定义全局控制器异常处理,
* @ExceptionHandler表示针对性异常处理,可对每种异常针对性处理。
*/
/**
* // 捕捉shiro的异常
* @param e
* @return
*/
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ExceptionHandler(value = ShiroException.class)
public Result handler(ShiroException e) {
log.error("运行时异常:----------------{}", e);
return Result.fail(401, e.getMessage(), null);
}
/**
* @Validated 校验异常错误
* @param e
* @return
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public Result handler(MethodArgumentNotValidException e) {
log.error("实体校验异常:----------------{}", e);
BindingResult bindingResult = e.getBindingResult();
ObjectError objectError = bindingResult.getAllErrors().stream().findFirst().get();
return Result.fail(objectError.getDefaultMessage());
}
/**
* 处理Assert的异常
* @param e
* @return
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(value = IllegalArgumentException.class)
public Result handler(IllegalArgumentException e) {
log.error("Assert异常:----------------{}", e);
return Result.fail(e.getMessage());
}
/**
* RuntimeException:捕捉其他异常
* @param e
* @return
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(value = RuntimeException.class)
public Result handler(RuntimeException e) {
log.error("运行时异常:----------------{}", e);
return Result.fail(e.getMessage());
}
}
7校验实体类
user实体类
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@NotBlank( message="昵称不能为空")
private String username;
private String avatar;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
private String email;
blog实体类
//校验时间
@JsonFormat(pattern = "yyy-MM-dd")
private LocalDateTime created;
8.跨域问题
1.非常的简单,只需要在controller上面
加上@CrossOrigin即可
2.也可以在config包下写全局跨域(我不喜欢写这么多代码,所以我用的上种注解方式)
/**
* 解决跨域问题
*/
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
.allowCredentials(true)
.maxAge(3600)
.allowedHeaders("*");
}
}
9.登录接口开发
@RestController
@CrossOrigin
public class loginController {
@Autowired
UserService userService;
@Autowired
JwtUtils jwtUtils;
@PostMapping("/login")
public Result login(@Validated @RequestBody loginDto loginDto, HttpServletResponse response) {
User user = userService.getOne(new QueryWrapper<User>().eq("username", loginDto.getUsername()));
Assert.notNull(user, "用户名不存在或密码错误");
if(!user.getPassword().equals(SecureUtil.md5(loginDto.getPassword()))){
return Result.fail("密码不正确");
}
String jwt = jwtUtils.generateToken(user.getId());
response.setHeader("Authorization", jwt);
response.setHeader("Access-control-Expose-Headers", "Authorization");
return Result.succ(MapUtil.builder()
.put("id", user.getId())
.put("username", user.getUsername())
.put("avatar", user.getAvatar())
.put("email", user.getEmail())
.map()
);
}
@RequiresAuthentication
@GetMapping("/logout")
public Result logout() {
SecurityUtils.getSubject().logout();
return Result.succ(null);
}
}
所在包package com.hu.my_web_site.common.dto下
@Data
@NoArgsConstructor
public class loginDto implements Serializable {
@NotBlank( message="用户名不能为空")
private String username;
@NotBlank( message="密码不能为空")
private String password;
}
10.博客接口的开发
@RestController
@CrossOrigin
public class BlogController {
@Autowired
private BlogService blogService;
//分页操作
@GetMapping("/blogs")
public Result list(@RequestParam(defaultValue = "1") Integer currentPage){
Page page = new Page(currentPage,5);
IPage pageData = blogService.page(page, new QueryWrapper<Blog>().orderByDesc("created"));
return Result.succ(pageData);
}
//通过id查询一条记录
@GetMapping("/blog/{id}")
public Result detail(@PathVariable Long id){
Blog blog = blogService.getById(id);
//判断是否为空
Assert.notNull(blog,"该内容为空");
return Result.succ(blog);
}
//修改或者添加
@PutMapping("/blog/edit")
public Result edit(@Validated @RequestBody Blog blog){
Blog temp;
if (blog.getId() != null){
//编辑
//通过id去拿blog详情
temp= blogService.getById(blog.getId());
// 只能编辑自己的文章
System.out.println(((AccountProfile) SecurityUtils.getSubject().getPrincipal()).getId());
Assert.isTrue(temp.getUserId().longValue() == ((AccountProfile) SecurityUtils.getSubject().getPrincipal()).getId(), "没有权限编辑");
}else{
//添加
temp = new Blog();
temp.setUserId(((AccountProfile) SecurityUtils.getSubject().getPrincipal()).getId());
temp.setCreated(LocalDateTime.now());
temp.setStatus(0);
}
BeanUtil.copyProperties(blog, temp, "id", "userId", "created", "status");
blogService.saveOrUpdate(temp);
return Result.succ(null);
}
//通过id去删除一条记录
@GetMapping("/delete/{id}")
private Result delete(@PathVariable("id") Long id){
boolean remove = blogService.removeById(id);
Assert.notNull(remove,"删除成功");
return Result.succ(remove);
}
}
后台接口完成
最后补充一下MybatisPlus分页插件,会用到这个
package com.hu.my_web_site.config;
@Configuration
@EnableTransactionManagement
@MapperScan("com.hu.my_web_site.mapper")
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
return paginationInterceptor;
}
}