java入门笔记六(springboot框架博客系统开发3)

继续完善这个博客的后端部分。

1 全局异常处理。新建com.blog.common.exception.GlobalExceptionHandler类。添加异常处理

package com.blog.common.exception;

import com.blog.common.lang.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.ShiroException;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.io.IOException;

/**
 * 全局异常处理
 */
@Slf4j
@RestControllerAdvice    //定义全局控制器异常处理,
public class GlobalExceptionHandler {
    // 处理shiro的异常
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(ShiroException.class)
    public Result handle401(ShiroException e){
        return  Result.fail(401,e.getMessage(),null);
    }
    //处理Assert的异常
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = IllegalArgumentException.class) //参数格式化异常
    public Result handler(IllegalArgumentException e) throws IOException{
        log.error("Assert异常:-------------->{}",e.getMessage());
        return Result.fail(e.getMessage());
    }
    //校验错误异常处理@validated
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Result handler(MethodArgumentNotValidException e) throws IOException{
        log.error("运行时异常:-------------->",e);
        BindingResult bindingResult=e.getBindingResult();
        ObjectError objectError =bindingResult.getAllErrors().stream().findFirst().get();
        return Result.fail(objectError.getDefaultMessage());
    }

    //其它运行是异常
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = RuntimeException.class)
    public Result handler(RuntimeException e) throws IOException{
        log.error("运行时异常:-------------->",e);
        return Result.fail(e.getMessage());
    }

}

2 添加数据实体处理校验,以user类为例,修改com.blog.entity.User类

package com.blog.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
import java.io.Serializable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;

/**
 * <p>
 * 
 * </p>
 *
 * @author 关注公众号:MarkerHub
 * @since 2021-02-15
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("m_user")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    @NotBlank(message = "昵称不能为空")
    private String username;

    private String avatar;
    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;

    private String password;

    private Integer status;

    private LocalDateTime created;

    private LocalDateTime lastLogin;

}

在com.blog.controller.UserController类中加一个测试方法

    /**
     * 测试实体校验
     * @param user
     * @return
     */
    @PostMapping("/testsave")
    public Object testUser(@Validated @RequestBody User user) {
        return user.toString();
    }

使用postman测试一下,提示类校验错误

 

3 后台加上全局处理跨域代码,新增com.blog.config.CorsConfig类

package com.blog.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 处理跨域问题
 */
@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("*");
    }
}

4 登录的接口,新增一个LoginDto类,新增com.blog.common.dto.LoginDto类

package com.blog.common.dto;

import lombok.Data;

import javax.validation.constraints.NotBlank;
import java.io.Serializable;

@Data
public class LoginDto implements Serializable {
    @NotBlank(message = "昵称不能为空")
    public String username;

    @NotBlank(message = "密码不能为空")
    private String password;
}

新增登录接口类com.blog.controller.AccountController类

package com.blog.controller;

import cn.hutool.core.map.MapUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.blog.common.dto.LoginDto;
import com.blog.common.lang.Result;
import com.blog.entity.User;
import com.blog.service.UserService;
import com.blog.utils.JwtUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;

@RestController
public class AccountController {

    @Autowired
    JwtUtils jwtUtils;
    @Autowired
    UserService userService;

    @CrossOrigin
    @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()
        );
    }
    // 退出
    @GetMapping("/logout")
    @RequiresAuthentication
    public Result logout() {
        SecurityUtils.getSubject().logout();
        return Result.succ(null);
    }

}

然后登录测试

错误1:登录接口报错java.lang.ClassNotFoundException: javax.xml.bind.DatatypeConverter

解决方法,去pom.xml添加依赖

   <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.0</version>
        </dependency>

maven更新依赖然后继续测试能够正常生成jwt

 

此时jwt已经生成,但是尚未存储到redis中

当header带着jwt访问其它接口时,如果接口验证权限注解@RequiresAuthentication

会走JwtFilter的onAccessDenied方法,将jwt写入redis

 

 

5 继续博客列表接口开发,新增BlogController

 

package com.blog.controller;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.blog.common.lang.Result;
import com.blog.entity.Blog;
import com.blog.service.BlogService;
import com.blog.utils.JwtUtils;
import com.blog.utils.ShiroUtils;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author 关注公众号:MarkerHub
 * @since 2021-02-15
 */
@RestController
@RequestMapping("/blog")
public class BlogController {

    @Autowired
    BlogService blogService;

    //查询博客列表
    @GetMapping("/blogs")
    public Result blogs(Integer currentPage){
        if(currentPage == null ||currentPage <1) currentPage=1;
        Page page=new Page(currentPage,5);
        IPage pagedata=blogService.page(page,new QueryWrapper<Blog>().orderByDesc("created"));
        return Result.succ(pagedata);
    }

    //获取博客详情
    @GetMapping("/blog/{id}")
    public Result detail(@PathVariable(name="id") long id){
        Blog blog=blogService.getById(id);
        Assert.notNull(blog,"不存在");
        return Result.succ(blog);
    }

    //编辑或者新增
    @RequiresAuthentication
    @PostMapping("blog/edit")
    public Result edit(@Validated @RequestBody Blog blog){
        Blog b=null;
        if(blog.getId()!=null){
            b=blogService.getById(blog.getId());
            Assert.isTrue(b.getUserId()== ShiroUtils.getProfile().getId(),"没有权限");
        }else {
            b=new Blog();
            b.setUserId(ShiroUtils.getProfile().getId());
            b.setCreated(LocalDateTime.now());
            b.setStatus(0);
        }
        BeanUtil.copyProperties(blog,b,"id","userId","created","status");
        blogService.saveOrUpdate(b);
        return Result.succ("成功");
    }


}

后台的代码基本上完成了,剩下前端的部分就不详细描写了,因为前端已经比较熟练了

前端和后端的代码一起放到了github上地址是:

下一篇写一下部署的过程。

 

posted @ 2023-06-06 13:49  阿拉蕾家的小铁匠  阅读(14)  评论(0编辑  收藏  举报