前言:

本文内容:Shiro整合Mybatis、Shiro请求授权实现、Shiro整合Thymeleaf

推荐免费SpringBoot基础教程视频:【狂神说Java】SpringBoot最新教程通俗易懂_哔哩哔哩_bilibili

Shiro整合Mybatis

  1. 在pom文件中引入需要的依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    <!--        mysql-->
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!-- log4j-->
    <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
    </dependency>
    <!-- druid-->
    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.8</version>
    </dependency>
    <!-- mybatis-->
    <dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
    </dependency>
    <!-- lombok-->
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    </dependency>
  2. 新建application.yaml并进行配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    spring:
    datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/mybatis?useSSL=true&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

    #SpringBoot默认是不注入这些的,需要自己绑定
    #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.Properity
    #则导入log4j 依赖就行
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
  3. resources下新建mapper文件夹

  4. application.properties

    1
    2
    3
    # 配置别名和mapper路径
    mybatis.type-aliases-package=com.jokerdig.pojo
    mybatis.mapper-locations=classpath:mapper/*.xml
  5. 在包pojo下新建User.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package com.jokerdig.pojo;

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

    /**
    * @author Joker大雄
    * @data 2022/7/27 - 11:23
    **/
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
    private int id;
    private String name;
    private String pwd;
    }
  6. mapper包下新建UserMapper.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package com.jokerdig.mapper;

    import com.jokerdig.pojo.User;
    import org.apache.ibatis.annotations.Mapper;
    import org.springframework.stereotype.Repository;

    /**
    * @author Joker大雄
    * @data 2022/7/27 - 11:26
    **/
    @Repository
    @Mapper
    public interface UserMapper {

    // 登录
    public User queryUserByName(String name);

    }
  7. resources/mapper下新建UserMapper.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?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.jokerdig.mapper.UserMapper">

    <!-- 登录-->
    <select id="queryUserByName" parameterType="String" resultType="User">
    select * from user where name=#{name}
    </select>

    </mapper>
  8. service包下新建UserService.javaUserServiceImpl.java

    UserService

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    package com.jokerdig.service;

    import com.jokerdig.pojo.User;

    /**
    * @author Joker大雄
    * @data 2022/7/27 - 11:31
    **/
    public interface UserService {
    // 登录
    public User queryUserByName(String name);
    }

    UserServiceImpl

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package com.jokerdig.service;

    import com.jokerdig.mapper.UserMapper;
    import com.jokerdig.pojo.User;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;

    /**
    * @author Joker大雄
    * @data 2022/7/27 - 11:32
    **/
    @Service
    public class UserServiceImpl implements UserService{
    @Autowired
    private UserMapper mapper;

    @Override
    public User queryUserByName(String name) {
    return mapper.queryUserByName(name);
    }
    }
  9. 修改UserRealm,使其连接数据库

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    package com.jokerdig.config;

    import com.jokerdig.pojo.User;
    import com.jokerdig.service.UserService;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.springframework.beans.factory.annotation.Autowired;

    /**
    * @author Joker大雄
    * @data 2022/7/26 - 11:12
    **/
    // 自定以 Realm extends AuthorizingRealm
    public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    System.out.println("执行了授权");

    return null;
    }

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

    UsernamePasswordToken userToken = (UsernamePasswordToken) token;
    // 通过数据库查到登录用户
    User user = userService.queryUserByName(userToken.getUsername());
    if(user==null){
    return null; //抛出异常 UnknownAccountException
    }
    // 密码认证 shiro来做
    return new SimpleAuthenticationInfo("",user.getPwd(),"");

    }
    }
  10. 运行测试,使用数据库的用户登录

Shiro请求授权实现

  1. 为数据库中的user表增加权限字段perms并为其中两个用户添加user:adduser:update权限

    1
    2
    3
    -- 添加perms字段
    alter table user
    add perms varchar(100) null;
  2. 修改pojo下的User,添加perms

    1
    private String perms;
  3. 在IndexController中添加未授权提示方法

    1
    2
    3
    4
    5
    6
    // 未授权提示
    @RequestMapping("/noauth")
    @ResponseBody
    public String unauthorized(){
    return "您没有权限访问该页面";
    }
  4. 完善UserRealm中的授权和认证

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    package com.jokerdig.config;

    import com.jokerdig.pojo.User;
    import com.jokerdig.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.session.Session;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.subject.Subject;
    import org.springframework.beans.factory.annotation.Autowired;

    /**
    * @author Joker大雄
    * @data 2022/7/26 - 11:12
    **/
    // 自定以 Realm extends AuthorizingRealm
    public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    System.out.println("执行了授权");
    // 为用户授权 SimpleAuthorizationInfo
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    // 获取当前登录对象
    Subject subject = SecurityUtils.getSubject();
    // 通过认证中SimpleAuthenticationInfo第一个值获取
    User currentUser = (User) subject.getPrincipal();
    // 通过数据库中用户的权限信息来授权
    info.addStringPermission(currentUser.getPerms());

    return info;
    }

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

    UsernamePasswordToken userToken = (UsernamePasswordToken) token;
    // 通过数据库查到登录用户
    User user = userService.queryUserByName(userToken.getUsername());
    if(user==null){
    return null; //抛出异常 UnknownAccountException
    }
    // 通过当前用户获取session并把user传递到前端
    Subject currentSubject = SecurityUtils.getSubject();
    Session session = currentSubject.getSession();
    session.setAttribute("loginUser",user);
    // 密码认证 shiro来做
    return new SimpleAuthenticationInfo(user,user.getPwd(),"");

    }
    }
  5. 完善ShiroConfig中的页面拦截和未授权页面

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    package com.jokerdig.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;

    import java.util.LinkedHashMap;
    import java.util.Map;

    /**
    * @author Joker大雄
    * @data 2022/7/26 - 11:04
    **/
    @Configuration
    public class ShiroConfig {

    // ShiroFileFactoryBean
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
    ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
    //设置安全管理器
    bean.setSecurityManager(defaultWebSecurityManager);

    // 添加shiro内置过滤器
    /*
    anon 无需认证可以访问
    authc 必须认证才能访问
    user 必须拥有记住我功能才能使用
    perms 拥有某个资源的权限才能访问
    role 拥有某个角色权限才能访问
    */
    Map<String,String> filterMap = new LinkedHashMap<>();

    // 授权
    // user:add user用户并且拥有add权限才能访问
    filterMap.put("/user/add","perms[user:add]");
    filterMap.put("/user/update","perms[user:update]");

    bean.setFilterChainDefinitionMap(filterMap);
    // 设置登录请求
    bean.setLoginUrl("/toLogin");
    // 设置未授权提示
    bean.setUnauthorizedUrl("/noauth");
    return bean;
    }

    // DefaultWebSecurityManager
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    // 关联UserRealm
    securityManager.setRealm(userRealm);
    return securityManager;
    }
    // 创建Realm 对象 需要自定义
    @Bean
    public UserRealm userRealm(){
    return new UserRealm();
    }

    }
  6. 运行测试

    授权的用户可以访问对应权限页面,未授权则无法访问

Shiro整合Thymeleaf

  1. 在pom文件中引入整合shiro依赖

    1
    2
    3
    4
    5
    6
    <!--        shiro整合thymeleaf-->
    <dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
    </dependency>
  2. ShiroConfig中添加整合方法ShiroDialect

    1
    2
    3
    4
    5
    // 整合ShiroDialect
    @Bean
    public ShiroDialect getShiroDialect(){
    return new ShiroDialect();
    }
  3. IndexController中添加注销登录方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // logout
    @RequestMapping("/logout")
    public String logout(){
    // 注销登录
    // 获取当前用户
    Subject subject = SecurityUtils.getSubject();
    subject.logout();
    return "redirect:index";
    }
  4. 修改index.html页面

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <!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>首页</title>
    </head>
    <body>
    <h1>首页</h1>
    <div th:if="${session.loginUser==null}">
    <a th:href="@{/toLogin}">登录</a>
    </div>
    <div th:if="${session.loginUser!=null}">
    <a th:href="@{/logout}">注销</a>
    </div>
    <p th:text="${msg}"></p>
    <hr>
    <div shiro:hasPermission="user:add">
    <a th:href="@{/user/add}">add</a>
    </div>
    <div shiro:hasPermission="user:update">
    <a th:href="@{/user/update}">update</a>
    </div>
    </body>
    </html>
  5. 运行测试

    根据用户权限展示对应的页面和注销功能的实现。