Shiro基础知识

目录

一、Shiro简介

        1、什么是Shiro

        2、Shiro架构

2、Springboot集成Shiro

        1、环境搭建

                1、搭建一个SpringBoot项目、选中web模块

                2、导入thymeleaf和shiro和spring整合的依赖

                3、自定义一个realm的类

                3、编写shiro的配置类

三、Shiro页面拦截+登录认证 

        1、在ShiorConfig中添加 拦截配置

        2、登录认证 

                1、编写一个登录的controller

                2、编写登录页面

                3、在UserRealm 中编写用户认证(验证)的判断逻辑

 四、Shiro整合Mybatis

         1、导入Mybatis的相关依赖

        2、编写 实体类

        3、在application.yaml中配置Druid数据源

        4、编写UserMapper

        5、编写service层

        6、编写UserMapper.xml

        7、在application.properties中绑定mapper.xml

        8、实现动态登录      

                        1、修改UserRealm中的认证 逻辑

五、授权

        1、在shiroConfig中增加拦截条件,指定未授权跳转位置

        2、编写未授权Controller

        3、在用户登录认证的时候,将用户放入Principal中

         4、在UserRealm 中添加授权的逻辑

六、Shiro整合Thymeleaf

        1、添加Maven依赖

        2、在Shiro的配置类中增加一个Bean

        3、修改首页前端配置

七、Shiro项目前后端分离项目实战

        1、编写Shiro配置类

        2、用户登录

        3、用户注销

八、日志系统

        1、自定义日志注解

        2、日志注解使用

        3、日志接口

         4、通用返回类


一、Shiro简介

        1、什么是Shiro

                1、Apache Shiro 是一个Java 的安全(权限)框架。
                2、Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE环境,也可以用在JavaEE环境。
                3、Shiro可以完成,认证,授权,加密,会话管理,Web集成,缓存等。

        2、Shiro架构

               Shiro三大对象:Subject        用户

                                        SecurityManager        管理所有用户

                                        Realm        连接数据

                Shiro面试必问

                 subject: 应用代码直接交互的对象是Subject,也就是说Shiro的对外API核心就是Subject,Subject代表了当前的用户,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等,与Subject的所有交互都会委托给SecurityManager;Subject其实是一个门面,SecurityManageer 才是实际的执行者
                SecurityManager:安全管理器,即所有与安全有关的操作都会与SercurityManager交互,并且它管理着所有的Subject,可以看出它是Shiro的核心,它负责与Shiro的其他组件进行交互,它相当于SpringMVC的DispatcherServlet的角色
                Realm:Shiro从Realm获取安全数据(如用户,角色,权限),就是说SecurityManager 要验证用户身份,那么它需要从Realm 获取相应的用户进行比较,来确定用户的身份是否合法;也需要从 Realm得到用户相应的角色、权限,进行验证用户的操作是否能够进行,可以把Realm看成DataSource;

2、Springboot集成Shiro

        1、环境搭建

                1、搭建一个SpringBoot项目、选中web模块

                2、导入thymeleaf和shiro和spring整合的依赖

       <!--thyemleaf模板引擎-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-java8time</artifactId>
        </dependency>
            <!--shiro整合spring-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.1</version>
        </dependency>

                3、自定义一个realm的类

package com.rk.config;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

//自定义Realm
public class UserRealm extends AuthorizingRealm {

    //执行授权逻辑
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了授权逻辑");
        return null;
    }


    //执行认证逻辑
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行了认证逻辑");
        return null;
    }
}

                        该类会在shiro配置类中使用

                3、编写shiro的配置类

package com.rk.config;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration//相当于是一个spring的配置文件
@ComponentScan("com.rk.config")//扫描包
public class shiroConfig {
    //1、创建 realm 对象 需要自定义
    @Bean//相当于一个bean  返回值类型相当于class属性值,方法名相当于id属性值
    public UserRealm userRealm(){
        return new UserRealm();//返回要注入到bean的对象
    }

    //2、创建 DefaultWebSecurityManager    @Qualifier("userRealm")自动装配 引入id值(方法名)
      @Bean(name="securityManager")//指定id值为securityManager
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager();
        //关联realm
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    //3、创建 ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")
                                                            DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
        //设置安全管理容器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        return shiroFilterFactoryBean;

    }
}

三、Shiro页面拦截+登录认证 

        需求:对访问add和update进行拦截

        

        1、在ShiorConfig中添加 拦截配置

        //添加shiro的内置过滤器
        /**
         * anon:无需认证就可以访问
         * authc:必须认证了才能访问
         * user:必须拥有 记住我 功能才能访问
         * perms:拥有对某个资源的权限才能访问
         * role:拥有某个角色权限才可以访问
         */
        Map<String,String> filterMap=new LinkedHashMap<>();
        filterMap.put("/user/add","authc");//认证才能访问
        filterMap.put("/user/update","authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
        //设置登录请求
        shiroFilterFactoryBean.setLoginUrl("/toLogin");//如果没有访问权限,自动跳转到登录页面

                toLogin是我们控制提前写好的

    @RequestMapping("/toLogin")
    public String tlogin(){
        return "login";
    }

        2、登录认证 

                1、编写一个登录的controller

    @RequestMapping("/login")
    public String login(String username, String password, Model model){
        //获取当前用户
        Subject subject = SecurityUtils.getSubject();
        //封装用户的登录数据
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        try {
            subject.login(token);//执行登录的方法 如果没有异常就ok
            return "index";
        } catch (UnknownAccountException e) {//用户名错误抛这个异常
            model.addAttribute("msg","用户名不存在");
            return "login";
        }catch (IncorrectCredentialsException e){//密码错误抛这个异常
            model.addAttribute("msg","密码错误");
            return "login";
        }
    }

                2、编写登录页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
<h1>登录</h1>
<form th:action="@{/login}">
    <p>用户名:<input type="text" name="username"></p>
    <p>密码:<input type="password" name="password"></p>
    <p><input type="submit" value="登录"></p>
</form>
<p style="color: red" th:text="${msg}"></p>
</body>
</html>

                3、在UserRealm 中编写用户认证(验证)的判断逻辑

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws       AuthenticationException {
        System.out.println("执行了认证逻辑");
        //伪造正确的用户名和密码 从数据库中读取
        String name="root";
        String password="123456";

        //判断用户名
        UsernamePasswordToken userToken=(UsernamePasswordToken) token;
        if(!userToken.getUsername().equals(name))//如果获取的用户名不等于数据库中的用户名
        {
            return null;//返回null就会抛异常 UnknownAccountException
        }

        //判断密码   密码认证  验证,不需要自己做  shiro帮我们做  只需要传入正确的密码
        return new SimpleAuthenticationInfo("",password,"");
    }

 

 

 四、Shiro整合Mybatis

         1、导入Mybatis的相关依赖

     <!-- 引入 myBatis,这是 MyBatis官方提供的适配 Spring Boot 的,而不是SpringBoot自己的-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/log4j/log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.12</version>
        </dependency>

        2、编写 实体类

package com.rk.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
}

        3、在application.yaml中配置Druid数据源

spring:
  datasource:
    username: root
    password: luolin123
    url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource #指定数据源


    #Spring Boot 默认是不注入这些属性值的,需要自己绑定
    #druid 数据源专有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true



    #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
    #如果允许时报错 java.lang.ClassNotFoundException:org.apache.log4j.Priority
    #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

        4、编写UserMapper

package com.rk.mapper;
import com.rk.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface UserMapper {
    public User findbyUsername(@Param("uname") String name);//根据名称查询用户

}

        5、编写service层

package com.rk.service.impl;
import com.rk.mapper.UserMapper;
import com.rk.pojo.User;
import com.rk.service.UserServie;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserServie {
    @Autowired
    private UserMapper userMapper;

    @Override
    public User findbyUsername(String name) {
        return userMapper.findbyUsername(name);
    }
}

        6、编写UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rk.mapper.UserMapper">
    <select id="findbyUsername" resultType="User" parameterType="String">
        select *
        from mybatis.user where  name=#{uname};
    </select>
</mapper>

        7、在application.properties中绑定mapper.xml

#指定myBatis的核心配置文件与Mapper映射文件
mybatis.mapper-locations=classpath:mapper/*.xml
# 注意:对应实体类的路径
mybatis.type-aliases-package=com.rk.pojo

        8、实现动态登录      

                        1、修改UserRealm中的认证 逻辑

                                从数据库中获取用户名和密码

    //执行认证逻辑
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了认证逻辑");

        //判断用户名
        UsernamePasswordToken userToken=(UsernamePasswordToken) token;
        //连接真实的数据库
        User user = userService.findbyUsername(userToken.getUsername());
        if(user==null){//没有这个人
            return null;
        }

        //判断密码   密码认证  验证,不需要自己做  shiro帮我们做  只需要传入正确的密码
        return new SimpleAuthenticationInfo("",user.getPwd(),"");
    }

五、授权

        1、在shiroConfig中增加拦截条件,指定未授权跳转位置

          //授权
        filterMap.put("/user/add","perms[user:add]");//代表请求必须有user:add这个权限才能访问
        filterMap.put("/user/update","perms[user:update]");
        //未授权页 请求/noauth页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/noauth");

        2、编写未授权Controller


    @RequestMapping("/noauth")
    @ResponseBody
    public String noAuth(){
        return "未经授权不能访问";
    }

        3、在用户登录认证的时候,将用户放入Principal中

 return new SimpleAuthenticationInfo(user,user.getPwd(),"");

         4、在UserRealm 中添加授权的逻辑

    //执行授权逻辑
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了授权逻辑");
        //给资源进行授权
        SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
        //添加资源的字符串授权
        Subject subject = SecurityUtils.getSubject();//获取当前对象
        User currentUser= (User) subject.getPrincipal();//拿到当前User对象
        info.addStringPermission(currentUser.getPerms());
        return info;
    }

        此时没有对应权限的用户无法 访问对应页面

六、Shiro整合Thymeleaf

        1、添加Maven依赖

<dependency>
        <groupId>com.github.theborakompanioni</groupId>
        <artifactId>thymeleaf-extras-shiro</artifactId>
        <version>2.0.0</version>
</dependency>

        2、在Shiro的配置类中增加一个Bean

    //整合ShiroDialect:整合shiro thymeleaf
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }

        3、修改首页前端配置

<body>
<h1>首页</h1>
<!--/*@thymesVar id="msg" type="ch"*/-->

<p th:text="${msg}"></p>
<div shiro:guest="true">
    <a th:href="@{/toLogin}">登录</a>
</div>


<hr>
<div shiro:hasPermission="user:add"><!--有add权限才会显示-->
    <a th:href="@{/user/add}">add</a>
</div>

<div shiro:hasPermission="user:update">
    <a th:href="@{/user/update}">update</a>
</div>

</body>

                 shiro:hasPermission="xxx":表示有xxx权限的此标签才会显示

                 shiro:guest="true":登录后隐藏此标签

        所以现在用户登录后只会显示对应权限的东西,登录成功后,登录按钮也会隐藏

七、Shiro项目前后端分离项目实战

        1、编写Shiro配置类

@Configuration
public class ShiroConfig {
    @Bean  //将MyRealm对象放到spring容器中
    public  MyRealm getMyRealm(){
        return new MyRealm();
    }
    @Bean//将DefaultWebSecurityManager对象放到spring容器中
    public DefaultWebSecurityManager getDefaultWebSecurityManager(){
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        //将getMyRealm归DefaultWebSecurityManager管理
        securityManager.setRealm(this.getMyRealm());
        return securityManager;
    }

    @Bean(name = "shiroFilterFactoryBean")  //将ShiroFilterFactoryBean对象放到spring容器中
    public ShiroFilterFactoryBean   shiroFilterFactoryBean(){
        ShiroFilterFactoryBean  filterFactoryBean=new ShiroFilterFactoryBean();
        filterFactoryBean.setSecurityManager(this.getDefaultWebSecurityManager());//注入上面的getSecurityManager获得SecurityManager对象
        Map<String,String>map=new LinkedHashMap<>(); //通过map设置过滤规则
        map.put("/**","anon"); //设置用户登录请求地址是匿名访问,不需要认证就可以访问资源
        filterFactoryBean.setFilterChainDefinitionMap(map);
        return filterFactoryBean;
    }
}

        2、用户登录

    @ApiOperation("登录")
    @SystemControllerLog(description ="登录")
    @GetMapping("/login")
    public CommonResult login(String username,String password){
        try {
            //加密验证
            UsernamePasswordToken token=new UsernamePasswordToken(username,
                    password);
            Subject subject= SecurityUtils.getSubject();
            if(!subject.isAuthenticated()){//如果没有登录
                subject.login(token);//该方法是shiro提供的登录方法,该方法会自动跳转到用户定义的Realm中
            }
        }catch (AuthenticationException uae) {
            System.out.println("用户名称或者密码不正确");
            return  CommonResult.failed();
        }
        return CommonResult.success(username);//登录成功返回username
    }

        传入username和password获取到token。如果没有登录就调用subject.login(token);执行真正的登录逻辑。调用MyRealm中的doGetAuthenticationInfo()方法进行登录验证。

//认证类,该类中有shiro提供的认证方法和授权方法,跟数据库的桥梁
public class MyRealm extends AuthorizingRealm {

    @Resource
    private UsersService usersService;
    //获得用户认证信息,其实从数据库中查询用户信息
    //认证方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username =(String)token.getPrincipal();//获得当前登录用户名称
        Users user=this.login(username);
        AuthenticationInfo  info=null;//封装到AuthenticationInfo类中从数据库中查询出来的用户信息
        if(user!=null){
            info=new SimpleAuthenticationInfo(user.getUname(),user.getPassword(),super.getName()) ;
           //查询出来用户信息放到session对象中
            SecurityUtils.getSubject().getSession().setAttribute("user",user);
        }
        return info;
    }

    private Users login(String username) {
        return usersService.login(username);
    }

    //授权方法,处理角色权限
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }
}

        首先传入username通过用户名查找用户。如果用户不为空,再进行密码校验。密码校验通过后,将该用户存入session。

UserService:  
  /**
     * 用户登录
     * @param username
     * @return
     */
    Users login(String username);

UserServiceImpl:
  @Override
    public Users login(String username) {
        return usersDao.login(username);
    }


mapper.xml
    <select id="login" resultType="com.book.app.entity.Users">
        select  * from users where uname =#{username}  
    </select>

        用户登录完成后,将用户名返回给前端。前端将用户名存入sessionStorage中,并在前端添加一个拦截器,每次路由跳转都会先判断sessionStorage中是否存在username,不存在就拦截跳转到登录界面。存在就放行。

        登录方法:

login() {
        this.$refs['user'].validate((valid) => {
          if (valid) {
            this.$http.get('/user/login',{
              params:this.user
            })
            .then(res=>{
                   if(res.data.data==null){
                         this.$message.error('你输入的用户名称或者密码不正确,或者账号被锁定,请联系管理员');
                   }else{
                     sessionStorage.setItem("username",res.data.data)
                      this.$router.push('/welcome');
                   }

            })
            .catch(e=>{
              this.$message.error('错了哦,网络异常');
            })
          }
        });
      }

        router下的index.js定义路由拦截器:

// 定义一个路由器   拦截登录
router.beforeEach((to,from,next)=>{
  if(to.path == '/login'){
    next();
  }else{
     if(sessionStorage.getItem("username")){
       next();
     }else{
       alert('你还未登录,请登录');
       this.$message.error('你还未登录,请登录!!');
       next({path:'/login'})
     }
  }
})

        3、用户注销

前端:

logout() {
        this.$http.get("/user/logout") //调用后台注销方法
          .then(res => {
            if (res.data.data == 200) {
              sessionStorage.removeItem("username"); //移除sessionStorage中的用户信息
              sessionStorage.clear(); //清空sessionStorage中的用户信息
              this.$router.push('/login'); //跳转到登录页面
            }
          })
          .catch(e => {
              this.$message.error('错了哦,网络异常');
          })
      }

        清空sessionStorage中的用户名。这样前端再次路由跳转的时候就会被拦截。

后端接口:

  @ApiOperation("用户注销")
    @GetMapping("/logout")
    public CommonResult logout(){
        SecurityUtils.getSubject().logout();
        return CommonResult.success(200);
    }

查看logout源码:

八、日志系统

        1、自定义日志注解

SystemControllerLog:
import java.lang.annotation.*;


@Target({ElementType.PARAMETER, ElementType.METHOD})//作用在参数和方法上
@Retention(RetentionPolicy.RUNTIME)//运行时注解
@Documented//表明这个注解应该被 javadoc工具记录
public @interface SystemControllerLog {
    String description() default "";
}
SystemLogAspect:
import com.rk.admin.domain.Logs;
import com.rk.admin.domain.User;
import com.rk.admin.service.LogsService;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;

@Aspect
@Component
public class SystemLogAspect {
    @Autowired
    private LogsService logsService;
    private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);
    //Controller层切点
    @Pointcut("@annotation(SystemControllerLog)")
    public void controllerAspect(){
    }

    @After("controllerAspect()")
    public void doBefore(JoinPoint joinPoint){
        HttpServletRequest request = ((ServletRequestAttributes)
                RequestContextHolder.getRequestAttributes()).getRequest();
        //获得当前用户登录信息
        //获取登录用户信息
        try {
            Logs logs = new Logs();
            //登录完成后将用户存入session 所以这里才能获取用户
            User user =(User)SecurityUtils.getSubject().getSession().getAttribute("user");
            logs.setOpername(user.getUsername());
            logs.setOpername("admin");
            logs.setMethods((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName()));
            logs.setDdesc(getControllerMethodDescription(joinPoint));
            logs.setIp(request.getRemoteAddr());//获得访问IP地址记录到日志的IP属性中
            logs.setOpertime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
            if ( logsService.save(logs)){
                System.out.println("添加日志成功");
            }else{
                System.out.println("添加日志失败");
            }
        }catch (Exception e){
            //记录本地异常日志
            logger.error("==前置通知异常==");
            logger.error("异常信息:{}",e.getMessage());
        }
    }

    /**
     * @Description  获取注解中对方法的描述信息 用于Controller层注解
     * @date 2020年11月9日 上午12:01
     */
    public static String getControllerMethodDescription(JoinPoint joinPoint) throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();//目标方法名
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        String description = "";
        for (Method method:methods) {
            if (method.getName().equals(methodName)){
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length==arguments.length){
                    description = method.getAnnotation(SystemControllerLog.class).description();
                    break;
                }
            }
        }
        return description;
    }
}

        有了这个自定义注解后,直接在需要添加日志的接口上添加该注解就好了,使用了spring的aop,每当执行其它接口之前,都会先执行doBefore()将日志新存储到数据库。

        这里从session中获取用户,获取操作人。

        2、日志注解使用

        3、日志接口

Controller:

import com.rk.admin.service.LogsService;
import com.rk.admin.tools.CommonResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;



@Api(tags = "日志模块")
@RestController
@RequestMapping("logs")
@CrossOrigin //前后端分离配置跨域访问
public class LogsController  {

    //服务对象
    @Autowired
    private LogsService logsService;

    @ApiOperation("分页查询日志信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page",value = "分页起始数据",required = true,example = "1"),
            @ApiImplicitParam(name = "limit",value = "每页显示分页最大数据",required = true,example = "10"),
            @ApiImplicitParam(name = "opername",value = "操作人姓名查询",example = "张三"),
    })
    @GetMapping("/findPager")
    public CommonResult findPager(Integer page, Integer limit, String opername){
        System.out.println("前端出的传递过来的参数:当前页码:"+page+"  每页条数:"+limit);
      return CommonResult.success(logsService.selectPage(page,limit,opername));
    }

Service:

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.rk.admin.domain.Logs;
import com.rk.admin.mapper.LogsMapper;
import com.rk.admin.service.LogsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class LogsServiceImpl implements LogsService {

    @Autowired
    private LogsMapper logsMapper;

    /**
     * 分页条件(操作名称)查询
     *
     * @param page
     * @param limit
     * @param opername
     * @return
     */
    @Override
    public IPage<Logs> selectPage(int page, int limit, String opername) {
        IPage<Logs> pageinfo=new Page<>(page,limit);
        LambdaQueryWrapper<Logs> lqw=new LambdaQueryWrapper<>();
        lqw.orderByDesc(Logs::getId);

        //如果操作名不为空
        if(opername!=null&&!opername.equals(""))
        {
            lqw.like(Logs::getOpername,opername);
        }

        //条件分页查询
        logsMapper.selectPage(pageinfo,lqw);

        return pageinfo;
    }

    /**
     * 保存日志
     *
     * @param logs
     * @return
     */
    @Override
    public Boolean save(Logs logs) {
       return logsMapper.insert(logs)>0;
    }
}

        这里使用了Mybatis-plus分页插件进行了分页

Mapper:

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.rk.admin.domain.Logs;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

@Repository
@Mapper
public interface LogsMapper extends BaseMapper<Logs> {

}

         4、通用返回类

通用返回工具类:

package com.rk.admin.tools;

/**
 * 通用返回对象
 * @param <T>
 */
public class CommonResult <T>{

    /**
     * 状态码
     */
    private long code;
    /**
     * 提示信息
     */
    private String message;
    /**
     * 数据封装
     */
    private T data;

    protected CommonResult() {
    }

    protected CommonResult(long code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    /**
     * 成功返回结果
     *
     * @param data 获取的数据
     */
    public static <T> CommonResult<T> success(T data) {
        return new CommonResult<T>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
    }

    /**
     * 成功返回结果
     *
     * @param data 获取的数据
     * @param  message 提示信息
     */
    public static <T> CommonResult<T> success(T data, String message) {
        return new CommonResult<T>(ResultCode.SUCCESS.getCode(), message, data);
    }

    /**
     * 失败返回结果
     * @param errorCode 错误码
     */
    public static <T> CommonResult<T> failed(IErrorCode errorCode) {
        return new CommonResult<T>(errorCode.getCode(), errorCode.getMessage(), null);
    }

    /**
     * 失败返回结果
     * @param errorCode 错误码
     * @param message 错误信息
     */
    public static <T> CommonResult<T> failed(IErrorCode errorCode,String message) {
        return new CommonResult<T>(errorCode.getCode(), message, null);
    }

    /**
     * 失败返回结果
     * @param message 提示信息
     */
    public static <T> CommonResult<T> failed(String message) {
        return new CommonResult<T>(ResultCode.FAILED.getCode(), message, null);
    }

    /**
     * 失败返回结果
     */
    public static <T> CommonResult<T> failed() {
        return failed(ResultCode.FAILED);
    }

    /**
     * 参数验证失败返回结果
     */
    public static <T> CommonResult<T> validateFailed() {
        return failed(ResultCode.VALIDATE_FAILED);
    }

    /**
     * 参数验证失败返回结果
     * @param message 提示信息
     */
    public static <T> CommonResult<T> validateFailed(String message) {
        return new CommonResult<T>(ResultCode.VALIDATE_FAILED.getCode(), message, null);
    }

    /**
     * 未登录返回结果
     */
    public static <T> CommonResult<T> unauthorized(T data) {
        return new CommonResult<T>(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data);
    }

    /**
     * 未授权返回结果
     */
    public static <T> CommonResult<T> forbidden(T data) {
        return new CommonResult<T>(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data);
    }

    public long getCode() {
        return code;
    }

    public void setCode(long code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}
IErrorCode接口:
package com.rk.admin.tools;

/**
 * 常用API返回对象接口
 */
public interface IErrorCode {
    /**
     * 返回码
     */
    long getCode();

    /**
     * 返回信息
     */
    String getMessage();
}

       

       

                

  

        

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
根据引用和引用,Shiro的配置文件可以通过引入依赖和注入相应的类来进行配置。在引用中提到了一个示例依赖,即org.apache.shiro:shiro-all:1.3.2。而在引用中提到了userRealm类的注入以及配置了一个credentialsMatcher方法,该方法对应的是RetryLimitHashedCredentialsMatcher类。RetryLimitHashedCredentialsMatcher继承自HashedCredentialsMatcher类,而HashedCredentialsMatcher实现了CredentialsMatcher接口,用于比较用户输入的密码和查询到的密码。所以,在Shiro的配置中,可以通过自定义的RetryLimitHashedCredentialsMatcher类来注入CredentialsMatcher。关于其他三个配置文件的内容,可以在下一篇文章中进行介绍。123 #### 引用[.reference_title] - *1* *2* [Shiro的学习之Shiro的配置(一)](https://blog.csdn.net/m0_54866636/article/details/126496465)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}} ] [.reference_item] - *3* [Shiro基础知识](https://blog.csdn.net/m0_46979453/article/details/121029363)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rk..

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值