springboot-mybatisPlus-Shiro集成

伯英武
2023-12-01

注意坑 springboot项目中如果有 aop的依赖时候,shiro的注解会失效

需要在配置类中,弄一个bean

@Bean
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
        
        /**
         * setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。
         * 在@Controller注解的类的方法中加入@RequiresRole等shiro注解,会导致该方法无法映射请求,导致返回404。
         * 加入这项配置能解决这个bug
         */
        defaultAdvisorAutoProxyCreator.setUsePrefix(true);
        return defaultAdvisorAutoProxyCreator;
    }

shiro安全框架 结合mybatisPlus和springboot整合

1.安全框架的shiro核心功能

  • Authentication: 认证 判断是否为合法用户 有时被称为 "登录”,这是证明用户合法

  • Authorization: 授权 前提:认证过了; 判断你是否有权访问某个资源

  • Session Management: session管理

  • Cryptography: 加解密

  • (支持功能,核心功能就上面4个)

  • Web Support: 支持 web

  • Caching: 支持缓存

  • Concurrency: 支持并发

  • Testing: 支持测试

  • "Run As": 用其他用户登录

  • "Remember Me": 记住我

简记

authc : Authentication 认证

authz: Authorization 授权

RBAC shiro的核心 基于角色的访问控制

授权基于角色实现(数据库的表)

role 角色 (表)

permission 权限 (表)

user 用户 (表)

都是多对多关系,所以还需要建立他们之间的关系表

user-role 用户角色关系表

role-permission 角色权限关系表

springboot-mybatisplus中集成shiro框架的步骤

1 、前期工作:

数据库建表 :用户表 ,角色表,权限表,角色权限关系表,用户角色关系表。。并且插入一些模拟数据

create table shiro_user
(
    user_id       int primary key auto_increment comment '用户ID',
    user_name     varchar(256) not null comment '用户名',
    user_password varchar(256) not null comment '用户密码',
    status        varchar(64)  not null comment '账户状态 Nomal|locked|Cansel'
​
) comment ='用户表';
​
​
​
create table shiro_role
(
    role_id       int primary key auto_increment comment '角色ID',
    role_name     varchar(256) not null comment '角色名'
​
) comment ='角色表';
​
​
​
​
create table shiro_user_role
(
    user_role_id       int primary key auto_increment comment '关系表ID',
    role_id      int not null comment '角色ID',
    user_id  int not null comment '用户ID'
) comment ='用户角色关系表';
​
create table shiro_permission
(
    permission_id       int primary key auto_increment comment '权限ID',
    permission_name     varchar(256) not null comment '权限名'
​
) comment ='权限表';
​
create table shiro_permission_role
(
    permission_role_id       int primary key auto_increment comment '关系表ID',
    role_id      int not null comment '角色ID',
    permission_id    int not null comment '权限ID'
) comment ='权限角色关系表';
​
​
# 插入用户
insert into  shiro_user VALUES (default,'tom','tom11','NOMAL');
insert into  shiro_user VALUES (default,'jery','jery11','NOMAL');
insert into  shiro_user VALUES (default,'victor','victor11','NOMAL');
insert into  shiro_user VALUES (default,'kangkang','kangkang11','NOMAL');
insert into  shiro_user VALUES (default,'xiaomei','xiaomei11','NOMAL');
​
# 插入角色
insert into  shiro_role VALUES (default,'student');
insert into  shiro_role VALUES (default,'teacher');
​
​
# 插入角色权限
insert into  shiro_permission VALUES (default,'doHomeWork');
insert into  shiro_permission VALUES (default,'talking');
insert into  shiro_permission VALUES (default,'say');
insert into  shiro_permission VALUES (default,'running');
insert into  shiro_permission VALUES (default,'teacher');
insert into  shiro_permission VALUES (default,'fireStudent');
insert into  shiro_permission VALUES (default,'downStudent');
​
# 给用户绑定角色
insert into  shiro_user_role VALUES (default,1,1);
insert into  shiro_user_role VALUES (default,1,2);
insert into  shiro_user_role VALUES (default,1,3);
insert into  shiro_user_role VALUES (default,2,4);
insert into  shiro_user_role VALUES (default,2,5);
# 给角色绑定权限
insert into  shiro_permission_role VALUES (default,1,1);
insert into  shiro_permission_role VALUES (default,1,2);
insert into  shiro_permission_role VALUES (default,1,3);
insert into  shiro_permission_role VALUES (default,1,4);
insert into  shiro_permission_role VALUES (default,2,5);
insert into  shiro_permission_role VALUES (default,2,6);
insert into  shiro_permission_role VALUES (default,2,7);
​
​

建立springboot-mybatis的web项目(选择spring Web依赖,lombok,MySQL Driver,Mybatis plus Framework,)

导入依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </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>
​
<!--mybatisplus-->
​
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>
<!--数据库的-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
​
​
<!--shiro安全框架的依赖-->
​
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-web-starter</artifactId>
            <version>1.4.1</version>
        </dependency>
​
​
        <!--swagger 自动开发文档依赖-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
        <!--代码生成器依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.3</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.1</version>
            <scope>test</scope>
        </dependency>
        <!--事务控制的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!--hutool依赖-->
        <!--字符串检查,雪花算法生成订单号,复制对象实体类,等等工具-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.5</version>
        </dependency>
        <!--aop依赖,切面编程用的(日志切面使用) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!--数据校验使用的-->
        <!--数据校验依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
​
​

导入自动代码生成器 放在test里面,用于自动生成数据库中表的实体类,以及相关代码

package org.victor.shirodemo_01;
​
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.victor.shirodemo_01.common.Log;
​
​
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
​
/**
 * @program: test_paper_project
 * @ClassName CreatIngAuotCode
 * @description:
 * @author: victorGang
 * @create: 2022-09-03 10:23
 * @Version 1.0
 **/
@Slf4j
public class CreatIngAuotCode {
​
​
//    放在测试目录里面!!!
​
​
    //表所在的数据库信息,密码
    public static final String JDBC_URL = "jdbc:mysql://127.0.0.1:3306/_0913shiro_01?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8";
    public static final String JDBC_USER = "root";
    public static final String JDBC_PASSWORD = "123456";
    //    作者  写自己名字就行
    public static final String AUTHOR = "VictorGang";
    //项目的根目录的!!上一级,上一级!!  复制根目录后把最后一个层级掐掉
    public static final String PARENT_PACKAGE = "org.victor";
    //    项目的目录__最后一级目录    上面掐掉的 那个级 单独放这里
    public static final String MODULE_NAME = "shirodemo_01";
    //表的抬头,数据库里面的   设计数据库表 以项目名 为前缀
    public static final String TABLE_PREFIX = "shiro_";
​
    @Log("根据数据库表自动生成一系列的类")
    public static void main(String[] args) {
        String projectPath = System.getProperty("user.dir") + "/src/main/";
        String javaPath = projectPath + "/java";  //自动生成类的存放路径
        String resourcePath = projectPath + "/resources/mappers";  //自动生成mapper.xml的存放路径
​
​
        FastAutoGenerator.create(JDBC_URL,
                JDBC_USER,
                JDBC_PASSWORD)
                .globalConfig(builder -> {
                    builder.author(AUTHOR)// 设置作者
                            .dateType(DateType.ONLY_DATE)
                            .disableOpenDir()//不打开文件夹
                            .enableSwagger()// 自动集成 那个swagger  但是不全,需要自己再添加一些,如果要搞这个,必须导入依赖
                            .outputDir(javaPath)
                            .fileOverride()//覆盖文件 如果之前有就把之前的覆盖掉,
​
                    ; // 指定输出目录
                })
                .packageConfig(builder -> {
                    builder.parent(PARENT_PACKAGE) // 设置父包名
                            .moduleName(MODULE_NAME) // 设置父包模块名
                            .entity("pojo")//设置实体类包名
                            .mapper("mapper")//设置mapper包名
                            .service("service")
                            .serviceImpl("service.impl")
                            .pathInfo(Collections.singletonMap(OutputFile.xml, resourcePath)); // 设置mapperXml生成路径
                })
                .strategyConfig((scanner,builder) -> {
                    builder.addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔?所有输入 all"))) // 设置需要生成的表名
                            .addTablePrefix(TABLE_PREFIX)
                            .serviceBuilder()
                            .formatServiceFileName("%sService") //去掉service的I,不搞这个会以service接口I开头
                            .controllerBuilder()
                            .enableRestStyle() //restful开启
                   // .entityBuilder().enableLombok()
                    //开启lombok
​
​
                    ; // 设置过滤表前缀
​
//设置生成的controller层的 注解和一些前缀
​
                })
                .templateEngine(new VelocityTemplateEngine())
                // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                .execute();
        log.info("执行完毕");
    }
​
    public static List<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 Arrays.asList(StringUtils.split(ipt, ","));
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "!");
    }
​
​
    // 处理 all 情况,把这个数据库所有的表,都一次性生成代码
    protected static List<String> getTables(String tables) {
        return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
    }
​
​
​
​
}

写配置

1 yml的配置

#端口的配置
server:
  port: 8081
​
​
#数据库的配置
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/_0913shiro_01?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
    password: 123456
    username: root
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
  mvc:
    format:
      date: yyyy-MM-dd HH:mm:ss
​
​
#mybatisPuls的配置
mybatis-plus:
  mapper-locations: "classpath:mappers/*.xml"
  configuration:
    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
    map-underscore-to-camel-case: true
  type-aliases-package: org.victor.shirodemo_01.pojo
​
#shiro的配置
shiro:
  loginUrl: "/login.html"
​
​
#日志配置
logging:
  pattern:
  level:
    org.victor.shirodemo_01: info
​
​
​
​

2 配置类

package org.victor.shirodemo_01.common;
​
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.victor.shirodemo_01.pojo.ShiroRealm;
​
​
/**
 * @program: shirodemo_01
 * @ClassName SecurityConfig
 * @description:
 * @author: victorGang
 * @create: 2022-09-14 15:12
 * @Version 1.0
 **/
@Configuration
public class SecurityConfig {
​
​
​
​
​
​
//安全框架的realm 提供数据源的,用来存储,从数据库获取的对象,,自己创建的
    @Bean
    public Realm shiroRealm(){
​
        return  new ShiroRealm();
    }
​
​
//    shiro的过滤链
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition(){
​
​
        DefaultShiroFilterChainDefinition sfcd=new DefaultShiroFilterChainDefinition();
//anon表示直接放过
        sfcd.addPathDefinition("/","anon");
        sfcd.addPathDefinition("/login","anon");
        sfcd.addPathDefinition("/login.html","anon");
        sfcd.addPathDefinition("/css","anon");
        sfcd.addPathDefinition("/js","anon");
        sfcd.addPathDefinition("/images","anon");
        sfcd.addPathDefinition("/fonts","anon");
        sfcd.addPathDefinition("/html","anon");
​
//不是anon表示需要进行拦截,进行校验和控制的
        sfcd.addPathDefinition("/logout","logout");
        sfcd.addPathDefinition("/**","user");
​
        return sfcd;
​
    }
​
​
    @Bean
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
​
        /**
         * setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。
         * 在@Controller注解的类的方法中加入@RequiresRole等shiro注解,会导致该方法无法映射请求,导致返回404。
         * 加入这项配置能解决这个bug
         */
        defaultAdvisorAutoProxyCreator.setUsePrefix(true);
        return defaultAdvisorAutoProxyCreator;
    }
​
​
​
}

3 Realm域对象的建立,就是上面那个bean里面。存储从数据库读取数据的对象

package org.victor.shirodemo_01.pojo;
​
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.victor.shirodemo_01.common.Log;
import org.victor.shirodemo_01.mapper.PermissionMapper;
import org.victor.shirodemo_01.mapper.RoleMapper;
import org.victor.shirodemo_01.mapper.UserMapper;
​
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
​
/**
 * @program: shirodemo_01
 * @ClassName ShiroRealm
 * @description:
 * @author: victorGang
 * @create: 2022-09-14 15:23
 * @Version 1.0
 **/
@Slf4j
public class ShiroRealm extends AuthorizingRealm {
​
    @Autowired
    private UserMapper userMapper;
​
    @Autowired
    private RoleMapper roleMapper;
​
    @Autowired
    private PermissionMapper permissionMapper;
​
​
    @Override
    @Log("校验用户角色权限")
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        log.info("从登录的获取,当前用户的信息,用这个用户去数据库查询角色和权限");
        User user = (User) principalCollection.getPrimaryPrincipal();
        if (user == null) {
            throw new AccountException("用户登录校验没有通过,不能进行此操作");
        }
​
​
        List<Role> roleList = roleMapper.selectByUserId(user.getUserId());
​
        Set<String> strRoles = roleList.stream()
                .map(r -> r.getRoleName())
                .collect(Collectors.toSet());
        List<String> permissions = new ArrayList<>();
​
        if (roleList.size() > 0) {
            //查找该用户所有的权限
            permissions = permissionMapper.selectPermInRoleIds(roleList);
        }else {
            throw new AccountException("该用户不具该权限");
        }
        SimpleAuthorizationInfo authzInfo = new SimpleAuthorizationInfo();
        authzInfo.setStringPermissions(new HashSet<>(permissions));
        authzInfo.setRoles(strRoles);
        return authzInfo;
    }
​
    @Override
    @Log("校验用户,去数据库查找输入的用户名对应的对象,返回给controller层的调用者")
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
​
​
        String username = (String) authenticationToken.getPrincipal();
​
        QueryWrapper<User> qw = new QueryWrapper<>();
        qw.eq("user_name", username);
        User user = userMapper.selectOne(qw);
        if (user != null) {
            return new SimpleAuthenticationInfo(user, user.getUserPassword(), getClass().getName());
        } else {
            throw new AccountException("用户不存在");
        }
    }
}

mapper.xml里面的去数据库查询用户,查询角色授权信息的

//查询用户的全部角色信息,返回角色的集合
<select id="selectByUserId" resultType="org.victor.shirodemo_01.pojo.Role">
​
        select sr.role_id, sr.role_name
        from shiro_user_role sur
​
                 join shiro_user su on su.user_id = sur.user_id
​
                 join shiro_role sr on sr.role_id = sur.role_id
        where sur.user_id = #{userid}
​
​
    </select>
        //查询角色的全部授权信息 ,返回的字符串集合
        
          <select id="selectPermInRoleIds" resultType="java.lang.String">
​
        SELECT sp.permission_name
        FROM shiro_permission_role spr
        join shiro_permission sp on spr.permission_id=sp.permission_id
        where spr.role_id in
        <foreach collection="list" item="demo" open="(" close=")">
            #{demo.roleId}
        </foreach>
​
    </select>
        

controller层的验证

package org.victor.shirodemo_01.controller;
​
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AccountException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.victor.shirodemo_01.common.ResultUtil;
​
/**
 * @program: shirodemo_01
 * @ClassName LoginController
 * @description:
 * @author: victorGang
 * @create: 2022-09-14 15:25
 * @Version 1.0
 **/
@RestController
@Slf4j
public class LoginController {
​
    @PostMapping("/login")
    public ResultUtil login(String username, String password) {
​
        Subject subject = SecurityUtils.getSubject();
​
        try {
            subject.login(new UsernamePasswordToken(username, password));
            return ResultUtil.success();
        } catch (AuthenticationException e) {
            if (e.getClass() == AccountException.class) {
                throw e;
            } else {
                throw new AccountException("用户名或者密码错误");
            }
​
​
        }
​
​
    }
​
    @GetMapping("/pay")
    public String pay() {
        return "success,已经登录成功,成功进入支付页面";
    }
​
    @GetMapping("/doHomeWork")
    @RequiresPermissions("doHomeWork")
    public ResultUtil doHomeWork() {
        return ResultUtil.success(200,"用户拥有doHomeWork权限");
    }
​
​
​
​
​
}

全局异常处理

package org.victor.shirodemo_01.controller.component;
​
​
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.ShiroException;
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.RestControllerAdvice;
import org.victor.shirodemo_01.common.MyException;
import org.victor.shirodemo_01.common.ResultUtil;
​
​
import java.io.IOException;
​
​
@RestControllerAdvice//restful风格,全局异常处理的,返回json
@Slf4j
public class GlobExceptionHandle {
​
​
    @ExceptionHandler(Exception.class)//全局处理系统异常
    public ResultUtil handleException(Exception e) {
//        e.printStackTrace();
        log.error("系统异常:" + e.getMessage(), e);
​
        return ResultUtil.error(001, "系统严重错误");
    }
​
​
​
    
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ResultUtil handler(MethodArgumentNotValidException e) throws IOException {
        /** @Author victorGang
         * @Description //TODO  全局异常处理,参数校验的,判断前端传过来的参数符不符合要求
         * @Date 19:47 2022/9/3
         * @Param [e]
         * @return org.victor.test_paper_project.common.ResultUtil
         **/
        BindingResult bindingResult = e.getBindingResult();
​
        ObjectError objectError = bindingResult.getAllErrors().stream().findFirst().get();
​
        String messages = objectError.getDefaultMessage();
​
        log.warn("MethodArgumentNotValidException异常:-------------->{}", messages);
​
        return ResultUtil.error().setData(messages);
    }
​
​
​
    /**
     * 拦截捕捉自定义异常 ConstraintViolationException.class
     * @param
     * @return
     */
​
//    @ExceptionHandler(value = ConstraintViolationException.class)
//    public ResultUtil ConstraintViolationExceptionHandler(ConstraintViolationException ex) {
//        Map map = new HashMap();
//        Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
//        Iterator<ConstraintViolation<?>> iterator = constraintViolations.iterator();
//        List<String> msgList = new ArrayList<>();
//        while (iterator.hasNext()) {
//            ConstraintViolation<?> cvl = iterator.next();
//            msgList.add(cvl.getMessageTemplate());
//        }
​
//
//        map.put("msg", msgList);
//        return ResultUtil.error(201,"参数校验异常").setData(map);
//    }
​
​
​
​
    @ExceptionHandler(ShiroException.class)//全局处理  安全框架异常
    public ResultUtil handleShiroException(ShiroException e) {
        log.warn("安全框架shiro异常:" + e.getMessage(), e);
​
        return ResultUtil.error(405, e.getMessage());
​
    }
​
​
    @ExceptionHandler(MyException.class)//全局处理  自定义异常
    public ResultUtil handleMyException(MyException e) {
        log.warn("业务异常:" + e.getMsg(), e);
​
        return ResultUtil.error(e.getCode(), e.getMsg());
​
    }
​
}
​

日志处理

​
package org.victor.shirodemo_01.common;
​
import java.lang.annotation.*;
​
/**
 * 自定义日志说明注解,用在Controller类的方法上
 * @author liwei
 * 只要controller层的方法上,标记这个自定义注解,并且把这个注解在注解切面里设置了,
 * 那么只要执行了这个方法,就会自动把这个方法,入参,执行的返回值,消耗时间,打印出来
 *
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
​
    /*
     * 业务名称
     */
    String value() default "";
​
}
​
​
​

日志切面

package org.victor.shirodemo_01.common;
​
​
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.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
​
/**
 * 接口执行日志切面类,前提:(1)需要确保类路径存在依赖Aspect-starer-aop(自己引入)和logback(spring自带了)。
*(2)自行注册到spring容器 @Component
 * @author liwei,lucas
 *
 */
@Aspect
@Slf4j
@Component
public class LogAspect {
​
    /**
     * 通过环绕通知在接口执行前后输出日志信息   把环绕通知,绑定给有自定义注解的方法。以自定义注解的全路径绑定
     * @param proceedingJoinPoint 
     * @return
     * @throws Throwable
     *
     */
    @Around("@annotation(org.victor.shirodemo_01.common.Log)")
    //环绕通知里面放切入点表达式,这里是直接放的自定义注解,只要被这个注解标记了的方法,就能执行这个环绕通知
    public Object logOutputInfo(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        MethodSignature ms = (MethodSignature)proceedingJoinPoint.getSignature();
        String operation = ms.getMethod().getAnnotation(org.victor.shirodemo_01.common.Log.class).value();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
​
        String finalOp = operation.trim().length() == 0 ? "" : "[" + operation + "]";
​
        log.info("执行业务"+ finalOp +",参数:{} ",proceedingJoinPoint.getArgs());
        Object result = proceedingJoinPoint.proceed();
        stopWatch.stop();
        log.info("业务方法"+ finalOp +"执行完毕返回数据: {},耗时{}ms",result,stopWatch.getTotalTimeMillis());
        return result;
    }
}
 类似资料: