Springboot整合Shiro框架入门

Springboot整合Shiro框架入门

  • 1、快速开始demo
  • 2、Springboot集成Shiro
    • 2.1、环境搭建
    • 2.2、shiro的拦截
    • 2.3、shiro的认证
    • 2.4、整合mybatis
    • 2.5、shiro进行授权
    • 2.6、shiro整合thymeleaf
  • 3、小结

1、快速开始demo

来到shiro的官网:shiro官方网站:
Springboot整合Shiro框架入门_第1张图片
Springboot整合Shiro框架入门_第2张图片
Springboot整合Shiro框架入门_第3张图片
下载官方在git仓库上提供的源码,下载后解压:
在这里插入图片描述
Springboot整合Shiro框架入门_第4张图片
官方提供了在多种场景下使用的demo,等一下会用到sample里面的东西。下面直接按官方推荐的10分钟入门demo进行测试。

创建一个总的父工程shiro-demo,在父工程下创建一个子模块shiro-demo-01,如下:
Springboot整合Shiro框架入门_第5张图片
父工程下什么都没有,src源目录删掉了,pom.xml也是空的:
Springboot整合Shiro框架入门_第6张图片
父工程pom什么依赖也没有,只包含一个子module。

子模块shiro-demo-01目录如下:
Springboot整合Shiro框架入门_第7张图片
总共只有4个东西,pom.xml如下:


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>shiro-demo-parentartifactId>
        <groupId>com.yczgroupId>
        <version>1.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <artifactId>shiro-demo-01artifactId>

    <dependencies>

        
        <dependency>
            <groupId>org.apache.shirogroupId>
            <artifactId>shiro-coreartifactId>
            <version>1.4.1version>
        dependency>

        
        <dependency>
            <groupId>org.slf4jgroupId>
            <artifactId>jcl-over-slf4jartifactId>
            <version>1.7.21version>
        dependency>

        <dependency>
            <groupId>org.slf4jgroupId>
            <artifactId>slf4j-log4j12artifactId>
            <version>1.7.21version>
        dependency>

        
        <dependency>
            <groupId>log4jgroupId>
            <artifactId>log4jartifactId>
            <version>1.2.17version>
        dependency>
    dependencies>

project>

log4j.properties和shiro.ini都是从sample目录里的quickstart里面直接复制粘贴过来的。
Springboot整合Shiro框架入门_第8张图片
日志配置文件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

shiro配置文件shiro.ini内容如下:


# -----------------------------------------------------------------------------
# Users and their assigned roles
#
# Each line conforms to the format defined in the
# org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions JavaDoc
# -----------------------------------------------------------------------------
[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

src源目录下的Qucikstart也是从quickstart里拷出来的,内容如下:

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.apache.shiro.mgt.SecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @Description
 * @ClassName Quickstart
 * @Author yanchengzhi
 * @date 2021.09.24 13:31
 */
public class Quickstart {

    private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);


    public static void main(String[] args) {


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

        // 获取当前用户对象Subject
        Subject currentUser = SecurityUtils.getSubject();

        // 通过当前用户获取Session对象
        Session session = currentUser.getSession();
        session.setAttribute("someKey", "aValue");
        String value = (String) session.getAttribute("someKey");
        if (value.equals("aValue")) {
            log.info("Shiro==Session===> [" + value + "]");
        }

        // 判断当前用户是否认证
        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?
            }
        }

        //getPrincipal方法获取用户认证信息
        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!");
        }

        //登出
        currentUser.logout();

        System.exit(0);
    }
}

运行main方法:
在这里插入图片描述
控制台打印的日志信息如上面,到这里快速开始的demo就运行成功了。

2、Springboot集成Shiro

下面测试在Springboot中集成Shiro框架,先进行环境的搭建。

2.1、环境搭建

在父工程shiro-demo下创建子模块shiro-boot,目录如下:
Springboot整合Shiro框架入门_第9张图片
(1)pom依赖


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>
    <groupId>com.yczgroupId>
    <artifactId>shiro-bootartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>shiro-bootname>
    <description>Demo project for Spring Bootdescription>

    <properties>
        <java.version>1.8java.version>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
        <spring-boot.version>2.3.7.RELEASEspring-boot.version>
    properties>

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-thymeleafartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-devtoolsartifactId>
            <optional>trueoptional>
            <scope>truescope>
        dependency>
        
        <dependency>
            <groupId>org.apache.shirogroupId>
            <artifactId>shiro-springartifactId>
            <version>1.7.1version>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintagegroupId>
                    <artifactId>junit-vintage-engineartifactId>
                exclusion>
            exclusions>
        dependency>
    dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-dependenciesartifactId>
                <version>${spring-boot.version}version>
                <type>pomtype>
                <scope>importscope>
            dependency>
        dependencies>
    dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <version>3.8.1version>
                <configuration>
                    <source>1.8source>
                    <target>1.8target>
                    <encoding>UTF-8encoding>
                configuration>
            plugin>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
                <version>2.3.7.RELEASEversion>
                <configuration>
                    <mainClass>com.ycz.shiroboot.ShiroBootApplicationmainClass>
                configuration>
                <executions>
                    <execution>
                        <id>repackageid>
                        <goals>
                            <goal>repackagegoal>
                        goals>
                    execution>
                executions>
            plugin>
        plugins>
    build>

project>

(2)application.properties配置

# 应用名称
spring.application.name=shiro-boot
# 应用服务 WEB 访问端口
server.port=8080
# THYMELEAF (ThymeleafAutoConfiguration)
# 开启模板缓存(默认值: true )
spring.thymeleaf.cache=false
# 检查模板是否存在,然后再呈现
spring.thymeleaf.check-template=true
# 检查模板位置是否正确(默认值 :true )
spring.thymeleaf.check-template-location=true
#Content-Type 的值(默认值: text/html )
spring.thymeleaf.content-type=text/html
# 开启 MVC Thymeleaf 视图解析(默认值: true )
spring.thymeleaf.enabled=true
# 模板编码
spring.thymeleaf.encoding=UTF-8
# 要被排除在解析之外的视图名称列表,⽤逗号分隔
spring.thymeleaf.excluded-view-names=
# 要运⽤于模板之上的模板模式。另⻅ StandardTemplate-ModeHandlers( 默认值: HTML5)
spring.thymeleaf.mode=HTML5
# 在构建 URL 时添加到视图名称前的前缀(默认值: classpath:/templates/ )
spring.thymeleaf.prefix=classpath:/templates/
# 在构建 URL 时添加到视图名称后的后缀(默认值: .html )
spring.thymeleaf.suffix=.html

(3)shiro配置

在config包下是关于Shiro的关键配置:
Springboot整合Shiro框架入门_第10张图片
先自定义所需的Realm类,只需继承AuthorizingRealm类即可,内容如下:

package com.ycz.shiroboot.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;

/**
 * @Description 自定义Realm,继承AuthorizingRealm类,重写认证和授权方法
 * @ClassName UserRealm
 * @Author yanchengzhi
 * @date 2021.09.25 15:36
 */
public class UserRealm extends AuthorizingRealm {

    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("进入授权方法!");
        return null;
    }

    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("进入认证方法!");
        return null;
    }
}

再配置ShiroConfig类,这个类主要是用来向Spring容器中注入bean的,内容如下:

package com.ycz.shiroboot.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;

/**
 * @Description Shiro的配置类
 * @ClassName ShiroConfig
 * @Author yanchengzhi
 * @date 2021.09.25 15:35
 */
@Configuration
public class ShiroConfig {

    /*
    * @description: 注入UserRealm的bean
    * @param: []
    * @return: com.ycz.shiroboot.config.UserRealm
    * @author: yanchengzhi
    * @date: 2021/9/25 15:42
    */
    @Bean
    public UserRealm userRealm() {
        return new UserRealm();
    }

    /*
     * @description: 注入DefaultWebSecurityManager的bean,需要依赖定义的UserRealm对象
     * @param: [userRealm]
     * @return: org.apache.shiro.web.mgt.DefaultWebSecurityManager
     * @author: yanchengzhi
     * @date: 2021/9/25 15:47
     */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 关联realm
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    /*
    * @description: 注入ShiroFilterFactoryBean的bean,需要依赖DefaultWebSecurityManager对象
    * @param: [defaultWebSecurityManager]
    * @return: org.apache.shiro.spring.web.ShiroFilterFactoryBean
    * @author: yanchengzhi
    * @date: 2021/9/25 15:52
    */
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(
            @Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
        // 设置安全管理器
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        return shiroFilterFactoryBean;
    }

}

(4)准备页面

准备所需的几个页面,目录层次如下:
Springboot整合Shiro框架入门_第11张图片
index.html直接在templates目录下,而add.html和edit.html在user目录下。

index.html内容如下:

DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>index页面title>
head>
<body>
    <h1>欢迎来到首页!h1>
    <h2 style="color: red;" th:text="${key}">h2>
    <a th:href="@{/user/add}">添加用户a> | <a th:href="@{/user/edit}">修改用户a>
body>
html>

add.html内容如下:

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户添加页面title>
head>
<body>
   <h1>添加页面h1>
body>
html>

edit.html内容如下:

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户修改页面title>
head>
<body>
   <h1>修改页面h1>
body>
html>

(5)控制器

在controller包下创建一个TestController类控制器:
在这里插入图片描述
具体内容如下:

package com.ycz.shiroboot.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @Description
 * @ClassName TestController
 * @Author yanchengzhi
 * @date 2021.09.25 15:10
 */
@Controller
@RequestMapping("/")
public class TestController {

    /*
    * @description: 默认跳转
    * @param: [model]
    * @return: java.lang.String
    * @author: yanchengzhi
    * @date: 2021/9/25 16:20
    */
    @RequestMapping("")
    public String toIndexPage(Model model) {
        model.addAttribute("key","Shiro安全框架");
        return "index";
    }

    /*
    * @description: 跳往添加页面
    * @param: []
    * @return: java.lang.String
    * @author: yanchengzhi
    * @date: 2021/9/25 16:21
    */
    @RequestMapping("/user/add")
    public String toAddPage() {
        return "/user/add";
    }
    
    /*
    * @description: 跳往编辑修改页面
    * @param: []
    * @return: java.lang.String
    * @author: yanchengzhi
    * @date: 2021/9/25 16:22
    */
    @RequestMapping("/user/edit")
    public String toEditPage() {
        return "/user/edit";
    }
}

(6)测试

启动Springboot项目,访问:http://localhost:8080/。
Springboot整合Shiro框架入门_第12张图片
成功跳转到了默认页面index.html,点击添加用户:
Springboot整合Shiro框架入门_第13张图片
Springboot整合Shiro框架入门_第14张图片
跳转到了添加页面,再点击修改用户:
在这里插入图片描述
Springboot整合Shiro框架入门_第15张图片
跳转到了修改页面。OK,到这里环境就搭建完成了,先保证项目能够跑得起来。下面将进一步的配置和测试。

2.2、shiro的拦截

shiro中请求的拦截是通过ShiroFilterFactoryBean对象来完成的,刚才其实在ShiroConfig中已经配置该类型的bean了,只不过有的配置内容没有加进去。下面在之前的基础上添加一些配置:

    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(
            @Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
        // 设置安全管理器
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        // 添加Shiro常用的过滤器
        /**
         *  常用过滤器如下:
         *  anon:无需认证即可访问
         *  authc:必须经过认证才能访问
         *  user:使用记住我后可访问
         *  perms:拥有某个资源的权限才可访问
         *  role:拥有某个角色才可访问
         *
         */
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
//        filterChainDefinitionMap.put("/user/add","authc");
//        filterChainDefinitionMap.put("/user/edit","authc");
        // 支持通配符配置
        filterChainDefinitionMap.put("/user/*","authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        // 设置登录页面,没有认证或权限的会被定位到此路径
        shiroFilterFactoryBean.setLoginUrl("/toLogin");
        return shiroFilterFactoryBean;
    }

然后在templates目录下创建一个登录页面login.html,内容如下:

DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登录页面title>
head>
<body>
   <h3>用户登录h3>
   <form th:action="@{/doLogin}" method="post">
       <p>用户名:<input type="text" name="username" />p>
       <p>密码:<input type="password" name="password" />p>
   form>
body>
html>

控制器中加一个跳转的方法:
Springboot整合Shiro框架入门_第16张图片
启动项目进行测试:
Springboot整合Shiro框架入门_第17张图片
点击添加用户:
Springboot整合Shiro框架入门_第18张图片
被定位到了登录页面。再点击修改用户:
Springboot整合Shiro框架入门_第19张图片
Springboot整合Shiro框架入门_第20张图片
同样的被定位到了登录页面。原因是在过滤器中的配置:
Springboot整合Shiro框架入门_第21张图片
/user/下的所有路径访问都需要经过认证,因为设置成了authc,对于没有认证的,将定位到登录Url。

2.3、shiro的认证

在控制器里面加一个登录校验的方法,如下:

    /*
    * @description: 登录校验
    * @param: [username, password, model]
    * @return: java.lang.String
    * @author: yanchengzhi
    * @date: 2021/9/25 17:16
    */
    @RequestMapping("/doLogin")
    public String doLogin(@RequestParam("username") String username,
                          @RequestParam("password") String password,
                          Model model) {
        // 获取当前用户
        Subject currentUser = SecurityUtils.getSubject();
        // 如果当前用户未进行认证
        if(!currentUser.isAuthenticated()) {
            // 将用户名和密码加密封装为token
            UsernamePasswordToken token = new UsernamePasswordToken(username,password);
            try {
                // 执行登录
                currentUser.login(token);
            } catch (UnknownAccountException uae) { // 用户名不存在
                model.addAttribute("errorMsg","用户名不存在!");
                return "login";
            } catch (IncorrectCredentialsException ice) { // 密码错误
                model.addAttribute("errorMsg", "密码错误!");
                return "login";
            }
        }
        return "index";
    }

登录页面login.html修改如下:
Springboot整合Shiro框架入门_第22张图片
登录的逻辑很简单,先获取到当前用户,如果用户没有进行认证,将前端提交过来的用户名和密码进行加密封装为一个token,然后用户带着这个token执行login方法登录。而login方法经过一系列的操作,最终会将这个token传到我们自定义UserRealm类里的doGetAuthenticationInfo方法里面:

    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("进入认证方法!");
        // 用户名和密码,应该从数据库中取,这里用静态数据代替
        String name = "ycz";
        String password = "ycz123456";
        // 将token进行转换,token是前端页面传过来的用户名+密码的加密封装
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        // 用户名校验
        if(!usernamePasswordToken.getUsername().equals(name)) {
            return null;  // 返回null的话,会自动抛出UnknownAccountException异常
        }
        // 密码校验,Shiro自动完成
        return new SimpleAuthenticationInfo("",password,"");
    }

然后在这个重写方法里进行用户名和密码的校验,用户名由我们自己校验,而密码则由Shiro来自动进行校验,非常安全和简单。启动项目访问进行测试。
在这里插入图片描述
先用错误的用户名进行登录:
Springboot整合Shiro框架入门_第23张图片
控制台:
在这里插入图片描述
再用正确的用户名和错误的密码进行登录:
Springboot整合Shiro框架入门_第24张图片
Springboot整合Shiro框架入门_第25张图片
控制台:
在这里插入图片描述
最后用正确的用户名和正确的密码进行登录:
Springboot整合Shiro框架入门_第26张图片
Springboot整合Shiro框架入门_第27张图片
控制台:
Springboot整合Shiro框架入门_第28张图片
登录成功,成功来到了首页。

2.4、整合mybatis

上面写的用户名和密码是死数据,实际上会从数据库中来查询。下面将mybatis整合进来,以便于从数据库中获取数据。

(1)pom依赖

在pom.xml中添加以下依赖坐标:

        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>8.0.11version>
        dependency>

        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druidartifactId>
            <version>1.1.12version>
        dependency>

        
        <dependency>
            <groupId>org.mybatis.spring.bootgroupId>
            <artifactId>mybatis-spring-boot-starterartifactId>
            <version>2.1.3version>
        dependency>

        
        <dependency>
            <groupId>log4jgroupId>
            <artifactId>log4jartifactId>
            <version>1.2.17version>
        dependency>

        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.20version>
            <scope>providedscope>
        dependency>

(2)application.yml全局配置

将前面的application.propertis内容全部放到新建的application.yml配置文件中,删除application.propertis,配置好的application.yml内容如下:

server:
  port: 8080

spring:
  application:
    name: shiro-boot
  ## 配置thymeleaf模板
  thymeleaf:
    cache: false
    check-template: true
    check-template-location: true
    servlet:
      content-type: text/html
    enabled: true
    encoding: UTF-8
    mode: HTML5
    prefix: classpath:/templates/
    suffix: .html
  ## 配置数据源
  datasource:
      url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: ycz123456
      # 初始连接数
      initialSize: 5
      # 最小连接池数量
      minIdle: 10
      # 最大连接池数量
      maxActive: 20
      # 配置获取连接等待超时的时间
      maxWait: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒
      minEvictableIdleTimeMillis: 300000
      # 配置一个连接在池中最大生存的时间,单位是毫秒
      maxEvictableIdleTimeMillis: 900000
      # 配置检测连接是否有效
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false

## mybatis配置
mybatis:
  ## 实体类路径
  type-aliases-package: com.ycz.domain
  ## 映射mapper位置
  mapper-locations: classpath:mapper/*.xml

(3)建表

mysql数据库中建user表:
在这里插入图片描述
表建好后,添加几条数据:
Springboot整合Shiro框架入门_第29张图片
(4)实体类

创建domain包,包下建立User实体类:
在这里插入图片描述

package com.ycz.shiroboot.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Description
 * @ClassName User
 * @Author yanchengzhi
 * @date 2021.09.25 18:48
 */
@Data
@AllArgsConstructor // 全参注解
@NoArgsConstructor // 无参注解
public class User {

    private Integer id;

    private String username;

    private String password;
}

(5)mapper层和映射文件

创建mapper包,包下创建UserMapper接口:
在这里插入图片描述

package com.ycz.shiroboot.mapper;

import com.ycz.shiroboot.domain.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

@Repository
@Mapper
public interface UserMapper {

    User findByUsername(@Param("username") String username);
}

在resources下创建mapper目录,下面创建UserMapper.xml映射文件:
在这里插入图片描述


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ycz.shiroboot.mapper.UserMapper">

    <select id="findByUsername" parameterType="String" resultType="com.ycz.shiroboot.domain.User">
        select * from user where username = #{username}
    select>
mapper>

(6)service层和实现类

创建service包,包下创建接口和实现类:
在这里插入图片描述

package com.ycz.shiroboot.service;

import com.ycz.shiroboot.domain.User;

public interface UserService {

    // 通过用户名查询用户
    User findByUsername(String username);
}
package com.ycz.shiroboot.service.impl;

import com.ycz.shiroboot.domain.User;
import com.ycz.shiroboot.mapper.UserMapper;
import com.ycz.shiroboot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @Description
 * @ClassName UserServiceImpl
 * @Author yanchengzhi
 * @date 2021.09.25 18:55
 */
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public User findByUsername(String username) {
        return userMapper.findByUsername(username);
    }
}

(7)修改UserRealm

修改自定义类UserRealm里的认证方法,现在从数据库中进行查询,而不使用死数据:

    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("进入认证方法!");
        // 将token进行转换,token是前端页面传过来的用户名+密码的加密封装
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        User user = userService.findByUsername(usernamePasswordToken.getUsername());
        if(user == null) { // 用户名校验
            return null;
        }
        // 密码校验,Shiro自动完成
        return new SimpleAuthenticationInfo("",user.getPassword(),"");
    }

(8)其他修改

过滤器里添加一个登出配置,登出是shiro自带的:
Springboot整合Shiro框架入门_第30张图片
首页加一个登出按钮:
Springboot整合Shiro框架入门_第31张图片
这个路径/shiro/logout不是我的控制器里加的,而是shiro提供的,直接用就可以了,退出后shiro会清掉用户的缓存。

(9)测试

启动项目,到登录页面进行测试:
Springboot整合Shiro框架入门_第32张图片
用数据库中不存在的用户名进行登录:
Springboot整合Shiro框架入门_第33张图片
用正确的用户名和错误密码进行登录:
Springboot整合Shiro框架入门_第34张图片
Springboot整合Shiro框架入门_第35张图片
最后用正确的用户名和正确的密码进行登录:
Springboot整合Shiro框架入门_第36张图片
Springboot整合Shiro框架入门_第37张图片
OK,登录成功,来到首页,说明整合mybatis是成功的。

2.5、shiro进行授权

用户认证成功后才会进行授权,也就是用户是先进行认证后进行授权的,以下将对shiro的授权进行测试。

数据库表加一个字段用来存权限,实际上权限是单独建表存的,这里为了简单,直接加在user表里。
Springboot整合Shiro框架入门_第38张图片
对应的User实体类也加一个属性:
Springboot整合Shiro框架入门_第39张图片
修改认证方法的返回:
在这里插入图片描述
这个的第一个参数改为user,也就是从数据库中获取出来的,也就是即将登录的用户信息。然后修改授权方法:

    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("进入授权方法!");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 获取当前登录用户
        Subject subject = SecurityUtils.getSubject();
        User currentUser = (User)subject.getPrincipal();
        info.addStringPermission(currentUser.getPers());
        return info;
    }

修改过滤器里的配置:
Springboot整合Shiro框架入门_第40张图片
这里添加了两条新的路径过滤配置,且采用了perms过滤器,即拥有权限才能访问。并且设置了没有权限的重定向url:
Springboot整合Shiro框架入门_第41张图片
控制器里加一个方法:

    /*
    * @description: 没有权限的跳转
    * @param: []
    * @return: java.lang.String
    * @author: yanchengzhi
    * @date: 2021/9/25 20:27
    */
    @ResponseBody
    @RequestMapping("/noAuth")
    public String noAuth() {
        return "您没有权限访问该资源!";
    }

启动项目进行测试:
Springboot整合Shiro框架入门_第42张图片
yanchengzhi这个用户是只有添加权限没有修改权限的,登录,点击添加用户:
Springboot整合Shiro框架入门_第43张图片
Springboot整合Shiro框架入门_第44张图片
可以访问,再点击修改用户:
Springboot整合Shiro框架入门_第45张图片
Springboot整合Shiro框架入门_第46张图片
退出,以另一个账号登录进去:
Springboot整合Shiro框架入门_第47张图片
云过梦无痕这个用户没有添加权限也没有修改权限。登录成功后点击添加用户:
Springboot整合Shiro框架入门_第48张图片
在这里插入图片描述
再点击修改用户:
Springboot整合Shiro框架入门_第49张图片
Springboot整合Shiro框架入门_第50张图片
测试是OK的,到这里说明权限的配置也是成功的,其他的账号就不进行测试了。

2.6、shiro整合thymeleaf

我们经常会在thymeleaf模板中根据权限信息来隐藏或显示某个按钮,这就需要在thymelaf中整合shiro,使用shiro标签进行控制。

(1)引入依赖

需要在pom中先引入thymelaf整合shiro的依赖:

        
        <dependency>
            <groupId>com.github.theborakompanionigroupId>
            <artifactId>thymeleaf-extras-shiroartifactId>
            <version>2.0.0version>
        dependency>

需要注意版本,可能会和其他包版本有冲突,注意一下即可。

(2)配置ShiroDialect

需要在ShiroConfig中配置shiro整合thymeleaf的方言:

    /*
    * @description: shiro整合thymeleaf
    * @param: []
    * @return: at.pollux.thymeleaf.shiro.dialect.ShiroDialect
    * @author: yanchengzhi
    * @date: 2021/9/25 20:46
    */
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }

(3)存Session

在用户登录成功后,可以往Session里存一些信息,这里的Session是Shiro提供的,而不是HttpSession。在登录方法加:
Springboot整合Shiro框架入门_第51张图片
登录成功后,将当前用户存在了Session里。

(4)模板中使用shiro标签

在thymelaf中使用shiro标签,需要先引入命名空间:
Springboot整合Shiro框架入门_第52张图片
然后再使用标签进行资源的显示和隐藏控制。修改后的index.html如下:

DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta charset="UTF-8">
    <title>index页面title>
head>
<body>
    <h1>欢迎来到首页!h1>
    <div th:if="${session.currentUser == null}">
        <a th:href="@{/toLogin}">登录a>
    div>
    <h2 style="color: red;" th:text="${key}">h2>
    <div shiro>div>
    <div shiro:hasPermission="user:add">
        <a th:href="@{/user/add}">添加用户a> |
    div>
    <div shiro:hasPermission="user:update">
        <a th:href="@{/user/edit}">修改用户a> |
    div>
    <a th:href="@{/shiro/logout}">退出a>
body>
html>

(5)测试

启动项目进行测试:
Springboot整合Shiro框架入门_第53张图片
现在没有登录,是看不到添加用户和修改用户的,点击登录,使用yanchengzhi账号登录进去:
Springboot整合Shiro框架入门_第54张图片
Springboot整合Shiro框架入门_第55张图片
只显示除了添加用户链接,退出,再以另一个账号登录:
Springboot整合Shiro框架入门_第56张图片
Springboot整合Shiro框架入门_第57张图片
只有修改用户。可以看到,这些都是使用shiro的标签来进行控制的,通过使用标签,来判断用户是否拥有某项权限,从而来限制用户的操作,这在实际开发中非常常见。

3、小结

这只是springboot整合shiro框架的一个简单入门,实际开发比这复杂的多,包括密码的加密,权限的配置拦截等,但是万变不离其宗,只要入门了,那么再复杂的都是以这为基础,学起来就会相对简单一点了。

你可能感兴趣的:(安全框架,spring,boot,maven,java)