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

                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":登录后隐藏此标签

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

                

  

        

猜你喜欢

转载自blog.csdn.net/m0_46979453/article/details/121029363