zoukankan      html  css  js  c++  java
  • Shiro

    上一篇博客介绍SpringBoot:集成SpringSecurity 地址:https://www.cnblogs.com/1693977889zz/p/14584718.html

    这篇文章主要介绍一个强大且以用的Java 安全框架 Shiro。Shiro拥有易于理解的API,可以很轻松快捷的搭建应用程序,它没有SpringSecurity的功能强大,但是执行身份验证、授权、密码和会话管理,web集成,缓存它都能做。下面我们来一起学习一下吧:

    image

    What is Apache Shiro?
    Apache Shiro is a powerful and easy to use Java security framework that offers developers an intuitive yet comprehensive solution to authentication, authorization, cryptography, and session management.

    In practical terms, it achieves to manage all facets of your application’s security, while keeping out of the way as much as possible. It is built on sound interface-driven design and OO principles, enabling custom behavior wherever you can imagine it. But with sensible defaults for everything, it is as “hands off” as application security can be. At least that’s what we strive for.

    image

    Mote

    image

    1.确定环境:JDK 1.8+ and Maven

    image

    2.The source can be cloned anonymously from Git with this command:(去 github 下载)

    git clone https://github.com/apache/shiro.git
    git checkout shiro-root-1.7.1 -b shiro-root-1.7.1
    

    3.found under samples/quickstart/src/main/java/Quickstart.java

    image

    4.模仿 quickstart 搭建项目

    image

    5.创建 spring-boot-shiro 项目,创建 Maven 模块 hello-shiro

    6.导入依赖

    <dependencies>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-core</artifactId>
                <version>1.7.1</version>
            </dependency>
    
            <!-- configure logging -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>jcl-over-slf4j</artifactId>
                <version>1.7.21</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>1.7.21</version>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
        </dependencies>
    

    7.创建 log4j.properties

    log4j.rootLogger=INFO, stdout
    
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
    
    # General Apache libraries
    log4j.logger.org.apache=WARN
    
    # Spring
    log4j.logger.org.springframework=WARN
    
    # Default Shiro logging
    log4j.logger.org.apache.shiro=INFO
    
    # Disable verbose logging
    log4j.logger.org.apache.shiro.util.ThreadContext=WARN
    log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
    

    8.创建 shiro.ini 文件

    [users]
    # user 'root' with password 'secret' and the 'admin' role
    root = secret, admin
    # user 'guest' with the password 'guest' and the 'guest' role
    guest = guest, guest
    # user 'presidentskroob' with password '12345' ("That's the same combination on
    # my luggage!!!" ;)), and role 'president'
    presidentskroob = 12345, president
    # user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz'
    darkhelmet = ludicrousspeed, darklord, schwartz
    # user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz'
    lonestarr = vespa, goodguy, schwartz
    
    # -----------------------------------------------------------------------------
    # Roles with assigned permissions
    # 
    # Each line conforms to the format defined in the
    # org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
    # -----------------------------------------------------------------------------
    [roles]
    # 'admin' role has all permissions, indicated by the wildcard '*'
    admin = *
    # The 'schwartz' role can do anything (*) with any lightsaber:
    schwartz = lightsaber:*
    # The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with
    # license plate 'eagle5' (instance specific id)
    goodguy = winnebago:drive:eagle5
    

    注意:ini 文件不高亮的话,记得安装插件

    image

    image

    9.导入 QuickStart.java

    马上报错:

    image

    方式一:修改导包:

    image

    虽然此时方法显示过时了,但是运行起来,并不会报错。

    image

    方式二:与时俱进,使用 DefaultSecurityManager

    image

    将如下代码:

    Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
    SecurityManager securityManager = factory.getInstance();
    

    替换为:

    DefaultSecurityManager securityManager = new DefaultSecurityManager();
            IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
            securityManager.setRealm(iniRealm);
    

    10.Run 测试 Ok!

    • 阅读 QuickStart.java 源码:

    Quickstart.java

    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.mgt.DefaultSecurityManager;
    import org.apache.shiro.realm.text.IniRealm;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.Subject;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class Quickstart {
    
        private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
    
    
        public static void main(String[] args) {
    
            //IniSecurityManagerFactory:已废弃
            //Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
            //SecurityManager securityManager = factory.getInstance();
    
            DefaultSecurityManager securityManager = new DefaultSecurityManager();
            IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
            securityManager.setRealm(iniRealm);
    
            SecurityUtils.setSecurityManager(securityManager);
    
    
            // Now that a simple Shiro environment is set up, let's see what you can do:
    
            // get the currently executing user:
            //通过SecurityUtils 得到当前用户的对象 Subject
            Subject currentUser = SecurityUtils.getSubject();
    
            // Do some stuff with a Session (no need for a web or EJB container!!!)
            //这个session不是web、EJB容器,它是Shiro自己的session!!!
            //通过当前用户拿到 session
            Session session = currentUser.getSession();
            //设置
            session.setAttribute("someKey", "aValue");
            String value = (String) session.getAttribute("someKey");
            if (value.equals("aValue")) {
                log.info("Retrieved the correct value! [" + value + "]");
            }
    
            // let's login the current user so we can check against roles and permissions:
            //判断当前用户是否被认证
            if (!currentUser.isAuthenticated()) {
                //Token:令牌
                UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
                token.setRememberMe(true);//记住我 功能
                try {
                    currentUser.login(token); //执行登录操作
                } catch (UnknownAccountException uae) {
                    log.info("There is no user with username of " + token.getPrincipal());//验证用户名
                } catch (IncorrectCredentialsException ice) {
                    log.info("Password for account " + token.getPrincipal() + " was incorrect!");//验证密码
                } catch (LockedAccountException lae) {
                    log.info("The account for username " + token.getPrincipal() + " is locked.  " +
                            "Please contact your administrator to unlock it.");//锁住用户
                }
                // ... catch more exceptions here (maybe custom ones specific to your application?
                catch (AuthenticationException ae) {
                    //unexpected condition?  error? 认证异常!
                }
            }
    
            //say who they are:
            //print their identifying principal (in this case, a username):
            //获得当前用户的认证
            log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
    
            //test a role: 获得该用户的角色,该角色拥有什么样的特权
            if (currentUser.hasRole("schwartz")) {
                log.info("May the Schwartz be with you!");
            } else {
                log.info("Hello, mere mortal.");
            }
    
            //test a typed permission (not instance-level)
            if (currentUser.isPermitted("lightsaber:wield")) {
                log.info("You may use a lightsaber ring.  Use it wisely.");
            } else {
                log.info("Sorry, lightsaber rings are for schwartz masters only.");
            }
    
            //a (very powerful) Instance Level permission:
            if (currentUser.isPermitted("winnebago:drive:eagle5")) {
                log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " +
                        "Here are the keys - have fun!");
            } else {
                log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
            }
    
            //all done - log out! //注销
            currentUser.logout();
            //结束
            System.exit(0);
        }
    }
    

    由上面 Quickstart.java 源码得出:

    Shiro 三大核心组件

    Subject, SecurityManager 和 Realms

    (下面的图片来自百度百科)
    image


    • SpringBoot 整合 Shiro

    1.创建 SpringBoot项目 shiro-springboot

    2.导入 Thymeleaf 依赖

    <!--Thymeleaf 模板引擎-->
    <dependency>
    	<groupId>org.thymeleaf</groupId>
    	<artifactId>thymeleaf-spring5</artifactId>
    </dependency>
    <dependency>
    	<groupId>org.thymeleaf.extras</groupId>
    	<artifactId>thymeleaf-extras-java8time</artifactId>
    </dependency>
    

    3.编写 index.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h3>首页</h3>
    
    <p th:text="${msg}"></p>
    </body>
    </html>
    ```C
    4.编写 Controller
    ```java
    package com.zhou.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    public class MyController {
    
        @RequestMapping({"/","index"})
        public String toIndex(Model model){
            model.addAttribute("msg","Hello,Shiro");
            return "index";
        }
    }
    
    

    5.Run 测试

    package com.zhou;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class ShiroSpringbootApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ShiroSpringbootApplication.class, args);
        }
    
    }
    

    image

    6.继续,导入 shiro-spring 依赖

    <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
    <dependency>
    	<groupId>org.apache.shiro</groupId>
    	<artifactId>shiro-spring</artifactId>
    	<version>1.7.1</version>
    </dependency>
    

    7.编写 Config

    a. 自定义 UserRealm

    package com.zhou.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;
    //自定义 UserRealm
    public class UserRealm extends AuthorizingRealm {
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("授权:doGetAuthorizationInfo");
            return null;
        }
    
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println("认证:doGetAuthenticationInfo");
            return null;
        }
    }
    

    b. 定义 ShiroConfig

    package com.zhou.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.Configuration;
    
    @Configuration
    public class ShiroConfig {
    
        //1.创建 realm 对象,需要自定义
        @Bean
        public UserRealm userRealm(){
            return new UserRealm();
        }
        //2.DefaultWebSecurityManager
        @Bean(name = "SecurityManager")
        public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
            DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
            //关联UserRealm
            defaultWebSecurityManager.setRealm(userRealm);
            return defaultWebSecurityManager;
        }
        //3.ShiroFilterFactoryBean
        @Bean
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            //设置安全管理器
            shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
            return shiroFilterFactoryBean;
        }
    }
    
    1. user 包下,创建三个页面(
      (SpringSecurity中有个默认的登录,这里没有,自己创建一个登录页 login.html)

    add.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>add</title>
    </head>
    <body>
    添加
    </body>
    </html>
    

    update.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>update</title>
    </head>
    <body>
    更新
    </body>
    </html>
    

    login.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>登录</title>
    </head>
    <body>
    <form>
        <input type="text" placeholder="用户名" name="username">
        <br><br>
        <input type="password" placeholder="密码" name="password">
        <br><br>
        <input type="submit">
    </form>
    </body>
    </html>
    

    9.Controller

    image

    @RequestMapping("/add")
    public String add(){
    	return "/user/add";
    }
    
    @RequestMapping("/update")
    public String update(){
    	return "/user/update";
    }
    
    @RequestMapping("/toLogin")
    public String toLogin(){
    	return "login";
    }
    
    1. index.html 添加代码
    <a th:href="@{/add}">add</a>
    <a th:href="@{/update}">update</a>
    

    11.Run 访问:http://localhost:8080/

    (一切正常,点击add、update 都能点进去)

    image


    • Shiro 实现登录拦截

    1.添加 Shiro的内置过滤

    image

        //3.ShiroFilterFactoryBean
        @Bean
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            //设置安全管理器
            shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
            //设置 shiro 的内置过滤器
            /*
            anon:无需认证就可以访问
            authc:必须认证才能访问
            user:必须拥有 记住我 功能才能用
            perms:拥有对某个资源的权限才能访问
            role:拥有某个角色权限才能访问
             */
            Map<String, String> filterMap = new LinkedHashMap<>();
            filterMap.put("/add","authc");
            filterMap.put("/update","authc");
    
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
    
            //设置登录的请求
            shiroFilterFactoryBean.setLoginUrl("/toLogin");
    
            return shiroFilterFactoryBean;
        }
    

    2.Run 访问:http://localhost:8080/ 并点击add、update

    发现都被拦截了,并跳到了登录页。


    • Shiro 实现用户认证

    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.login.html 添加验证失败信息

    <p th:text="${msg}" style="color: red"></p>
    

    3.访问:随便输入用户名和密码,观察控制台

    image

    查看 AuthenticationInfo,发现它是一个接口:

    image

    new SimpleAuthenticationInfo 的三个参数:

    image

    4.伪造一个用户和密码

        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println("认证:doGetAuthenticationInfo");
            //用户名、密码 数据库中取(这里先伪造一下)
            String name="zhouzhou";
            String password="1234";
            UsernamePasswordToken userToken=(UsernamePasswordToken)authenticationToken;
    
            if(!userToken.getUsername().equals(name)){
                return null;//抛出异常 UnknownAccountException
            }
            //密码认证,Shiro来做
            return new SimpleAuthenticationInfo("",password,"");
        }
    }
    

    5.Run 测试 OK!

    正确的用户名和密码才可以登录成功。


    • Shiro 整合 Mybatis

    1.添加依赖

    <dependency>
    	<groupId>org.projectlombok</groupId>
    	<artifactId>lombok</artifactId>
    </dependency>
    <dependency>
    	<groupId>mysql</groupId>
    	<artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
    	<groupId>org.mybatis.spring.boot</groupId>
    	<artifactId>mybatis-spring-boot-starter</artifactId>
    	<version>2.1.3</version>
    </dependency>
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
    	<groupId>com.alibaba</groupId>
    	<artifactId>druid</artifactId>
    	<version>1.2.5</version>
    </dependency>
    <dependency>
    	<groupId>log4j</groupId>
    	<artifactId>log4j</artifactId>
    	<version>1.2.17</version>
    </dependency>
    

    注意,maven 资源过滤问题
    (这里用了通配符,就不逐一添加 include 的了!因为之后整合太多东西,经常报错:Error resolving template [login], template might not exist or might not be a...,因为这些排错找了很久。)

     <!--在build中配置resources,来防止我们资源导出失败的问题-->
     <resources>
     	<resource>
     		<directory>src/main/resources</directory>
     			<includes>
     				<include>**/*.*</include>
     			</includes>
     			<filtering>false</filtering>
    	 </resource>
     	<resource>
     		<directory>src/main/java</directory>
    		 	<includes>
     				<include>**/*.*</include>
     			</includes>
     			<filtering>false</filtering>
     	</resource>
     </resources>
    

    2.创建数据库

    CREATE DATABASE `mybatis`
    
    USE `mybatis`
    
    DROP TABLE IF EXISTS `user`;
    
    CREATE TABLE `user` (
      `id` INT(11) NOT NULL AUTO_INCREMENT,
      `name` VARCHAR(255) NOT NULL,
      `pwd` VARCHAR(255) NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8;
    

    3.插入一些数据,并让 idea 连上数据库

    image

    4.配置文件

    application.yaml

    spring:
      thymeleaf:
        cache: true
    
      datasource:
        username: root
        password: root
        url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8
        driver-class-name: com.mysql.cj.jdbc.Driver
        type: com.alibaba.druid.pool.DruidDataSource #Druid
    
        #Spring Boot 默认是不注入这些属性值的,需要自己绑定
        #druid 数据源专有配置
        initialSize: 4
        minIdle: 4
        maxActive: 20
        maxWait: 30000
        timeBetweenEvictionRunsMillis: 50000
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        poolPreparedStatements: true
    
        #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
        filters: stat,wall,log4j
        maxPoolPreparedStatementPerConnectionSize: 20
        useGlobalDataSourceStat: true
        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
    

    application.properties

    mybatis.type-aliases-package=com.zhou.pojo
    mybatis.mapper-locations=classpath:mapper/*.xml
    
    #声明thymeleaf使用非严格的html。
    spring.thymeleaf.content-type=text/html
    spring.thymeleaf.mode=LEGACYHTML5
    spring.thymeleaf.cache=false
    

    5.创建实体类

    package com.zhou.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import java.io.Serializable;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User implements Serializable{
        private int id;
        private String name;
        private String pwd;
    }
    

    6.mapper

    UserMapper 接口

    package com.zhou.mapper;
    
    import com.zhou.pojo.User;
    import org.apache.ibatis.annotations.Mapper;
    import org.springframework.stereotype.Repository;
    
    @Mapper
    @Repository
    public interface UserMapper {
        public User queryUserByName(String name);
    }
    

    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.zhou.mapper.UserMapper">
        <select id="queryUserByName" parameterType="String" resultType="User">
            select * from mybatis.user where name=#{name}
        </select>
    </mapper>
    

    7.service

    UserService 接口

    package com.zhou.service;
    
    import com.zhou.pojo.User;
    
    public interface UserService {
        public User queryUserByName(String name);
    }
    

    UserServiceImpl 实现类

    package com.zhou.service;
    
    import com.zhou.mapper.UserMapper;
    import com.zhou.pojo.User;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserServiceImpl implements UserService {
        @Autowired
        UserMapper userMapper;
        @Override
        public User queryUserByName(String name) {
            return userMapper.queryUserByName(name);
        }
    }
    

    8.Test 测试

    package com.zhou;
    
    import com.zhou.service.UserServiceImpl;
    import org.junit.jupiter.api.Test;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    
    @SpringBootTest
    class ShiroSpringbootApplicationTests {
    
        @Autowired
        UserServiceImpl userService;
    
        @Test
        void contextLoads() {
            System.out.println(userService.queryUserByName("大可"));
        }
    }
    
    

    9.成功,查到数据

    image


    • 连接真实的数据库

    1.修改 AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)方法

    image

     @Autowired
        UserService userService;
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println("认证:doGetAuthenticationInfo");
            //用户名、密码 数据库中取(这里先伪造一下)
            //String name="zhouzhou";
            //String password="1234";
    
            UsernamePasswordToken userToken=(UsernamePasswordToken)authenticationToken;
            //连接真实的数据库
            User user = userService.queryUserByName(userToken.getUsername());
    
            if(user==null){
                return null;//没有这个人
            }
            //密码认证,Shiro来做。可以加密 MD5、MD5盐值加密
            return new SimpleAuthenticationInfo("",user.getPwd(),"");
        }
    

    2.Run 测试

    用数据库里里面的数据测试,OK!


    • Shiro 请求授权实现

    首先,定制简单的请求授权

    1.ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager)方法中设置

    image

    //授权
    filterMap.put("/add","perms[add]");
    filterMap.put("/update","perms[update]");
    

    2.Run 测试

    此时,update 和 add 不可访问了,并报 type=Unauthorized, status=401

    image

    • 可以自己设置未授权页面

    1.Controller

        //未授权页面
        @RequestMapping("/unauthorized")
        @ResponseBody
        public String getUnauthorized(){
            return "您未授权,不可访问!";
        }
    

    2.ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager)方法中添加设置

    image

    //设置未授权的页面
    shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
    

    3.Run 测试

    image

    再者,将授权赋值给用户(难点)
    在 AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection)方法中设置

    image

        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("授权:doGetAuthorizationInfo");
    
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            simpleAuthorizationInfo.addStringPermission("add");
            return simpleAuthorizationInfo;
        }
    

    Run 测试 发现 每个登录成功的用户,都被授权了。

    最后,在数据库给用过增加权限设置

    1.增加 perms

    image

    2.在 数据库 中给用户添加权限

    image

    3.修改 实体类

    package com.zhou.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import java.io.Serializable;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User implements Serializable{
        private int id;
        private String name;
        private String pwd;
        private String perms;
    }
    

    4.修改 自定义 UserRealm

    image

    package com.zhou.config;
    
    import com.zhou.pojo.User;
    import com.zhou.service.UserService;
    import org.apache.shiro.SecurityUtils;
    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.apache.shiro.subject.Subject;
    import org.springframework.beans.factory.annotation.Autowired;
    
    //自定义 UserRealm
    public class UserRealm extends AuthorizingRealm {
        @Autowired
        UserService userService;
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("授权:doGetAuthorizationInfo");
    
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            //simpleAuthorizationInfo.addStringPermission("add");
            //simpleAuthorizationInfo.addStringPermission("update");
            //拿到当前登录的这个对象
            Subject subject = SecurityUtils.getSubject();
            //拿到User对象
            User currentUser = (User)subject.getPrincipal();
            //设置当前用户的权限(要到数据库去拿)
            simpleAuthorizationInfo.addStringPermission(currentUser.getPerms());
            return simpleAuthorizationInfo;
        }
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println("认证:doGetAuthenticationInfo");
            UsernamePasswordToken userToken=(UsernamePasswordToken)authenticationToken;
            //连接真实的数据库
            User user = userService.queryUserByName(userToken.getUsername());
            if(user==null){ return null; }
            return new SimpleAuthenticationInfo(user,user.getPwd(),"");
        }
    }
    

    5.Run 测试 到数据库找用户,测试一波,完全OK。


    • Shiro 整合 thymeleaf

    1.导入依赖

    image

    <!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
    <dependency>
        <groupId>com.github.theborakompanioni</groupId>
        <artifactId>thymeleaf-extras-shiro</artifactId>
        <version>2.0.0</version>
    </dependency>
    

    2.配置 Bean
    (放在 ShiroConfig 类中即可)

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

    3.修改 index.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org"
                    xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h3>首页</h3>
            <p shiro:notAuthenticated>
                <a th:href="@{/toLogin}">登录</a>
            </p>
            <p th:text="${msg}"></p>
            <div shiro:hasPermission="add">
                <a th:href="@{/add}">add</a>
            </div>
            <div shiro:hasPermission="update">
                <a th:href="@{/update}">update</a>
            </div>
    </body>
    </html>
    

    4.Run 测试 OK!

    image

    个人学习笔记,针对本人在自学中遇到的问题。

  • 相关阅读:
    day08超市商品库存案例
    day07
    day06_03
    day06_02
    day06_01
    最简单的库存管理java案例
    Day05_homework
    方法的使用注意事项
    day05
    冒泡排序
  • 原文地址:https://www.cnblogs.com/1693977889zz/p/14590714.html
Copyright © 2011-2022 走看看