Apache Shiro,这一篇就够了

news2024/9/23 1:40:55

Apache Shiro,这一篇就够了

  • 1.Shiro实现登录拦截
  • 2.登录认证操作
  • 3.Shiro整合Mybatis
  • 4.用户授权操作
  • 5.Shiro授权
  • 6.Shiro整合Thymeleaf

1.Shiro实现登录拦截

前期环境准备

准备添加Shiro的内置过滤器:

@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
    ShiroFilterFactoryBean subject = new ShiroFilterFactoryBean();
    // 设置安全管理器
    // 需要关联 securityManager ,通过参数把 securityManager 对象传递过来
    subject.setSecurityManager(defaultWebSecurityManager);
    /*
        添加Shiro内置过滤器,常用的有如下过滤器:
        anon: 无需认证就可以访问
        authc: 必须认证才可以访问
        user: 如果使用了记住我功能就可以直接访问
        perms: 拥有某个资源权限才可以访问
        role: 拥有某个角色权限才可以访问
     */
    Map<String, String> filterMap = new LinkedHashMap<String, String>();
    filterMap.put("/user/add", "authc");
    filterMap.put("/user/update", "authc");
    subject.setFilterChainDefinitionMap(filterMap);
    return subject;
}

再次启动项目,拦截成功!

在这里插入图片描述

点击后会跳转到一个login.jsp页面,这个不是我们想要的效果,我们需要自己定义一个login页面!

我们编写一个自己的login页面:

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

编写跳转的controller:

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

在shiro中配置一下! ShiroFilterFactoryBean() 方法下面:

// 修改到要跳转的login页面;
subject.setLoginUrl("/toLogin");

再次测试,成功的跳转到了我们指定的Login页面!

在这里插入图片描述

另外,我们可以优化一下代码,拦截路径可以使用通配符来操作,更加简洁:

filterMap.put("/user/*","authc");

2.登录认证操作

编写一个登录的controller:

@RequestMapping("/login")
public String login(String username, String password, Model model) {
    // 使用shiro,编写认证操作
    // 1. 获取Subject
    Subject subject = SecurityUtils.getSubject();
    // 2. 封装用户的数据
    UsernamePasswordToken token = new UsernamePasswordToken(username,
            password);
    // 3. 执行登录的方法,只要没有异常就代表登录成功!
    try {
        subject.login(token); // 登录成功!返回首页
        return "index";
    } catch (UnknownAccountException e) { // 用户名不存在
        model.addAttribute("msg", "用户名不存在");
        return "login";
    } catch (IncorrectCredentialsException e) { // 密码错误
        model.addAttribute("msg", "密码错误");
        return "login";
    }
}

接下来:

在前端修改对应的信息输出或者请求!

登录页面增加一个 msg 提示:

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

给表单增加一个提交地址:

<form th:action="@{/login}">

此时运行项目测试,成功执行了认证的模块:

在这里插入图片描述

现在,重要的来了,在 UserRealm 中编写用户认证的判断逻辑:

public class UserRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了授权方法");
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String name = "admin";
        String password = "123456";
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        // 用户名认证
        if (!userToken.getUsername().equals(name)) {
            // 用户名不存在
            // shiro底层就会抛出 UnknownAccountException异常
            return null;
        }
        /*
            密码认证
            验证密码,我们可以使用一个AuthenticationInfo实现类SimpleAuthenticationInfo
            shiro会自动帮我们验证!重点是第二个参数就是要验证的密码!
         */
        return new SimpleAuthenticationInfo("", password, "");
    }
}

3.Shiro整合Mybatis

导入Mybatis相关依赖:

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.12</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.15</version>
</dependency>

<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
    <scope>provided</scope>
</dependency>

编写配置文件 - 连接配置 application.yml

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/klza?useSSL=false&serverTimezone=GMT%2B8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

    initialSize: 5 # 初始化连接池大小
    minIdle: 5 # 最小值
    maxActive: 20 # 最大值
    maxWait: 60000  # 最长连接等待时间
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000 # 连接保持空闲而不被驱逐的最长时间
    validationQuery: SELECT 1 FROM DUAL #用来检测连接是否有效的sql
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

    # 配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
    # 如果允许报错,java.lang.ClassNotFoundException: org.apache.Log4j.Property
    # 则导入log4j 依赖就行
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

# mybatis配置
mybatis:
  type-aliases-package: com.klza.pojo  # 实体类所在的包路径
  mapper-locations: classpath*:mapper/*.xml     # mybatis的映射文件所在的包路径    

编写实体类:(和数据库中的表字段要对应哦)

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String username;
    private String password;
}

编写Mapper接口:

@Repository
@Mapper
public interface UserMapper {
    User queryUserByName(String username);
}

编写Mapper配置文件:

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

编写UserService 层:

接口:

public interface UserService {
    User queryUserByName(String name);
}

实现类:

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserMapper userMapper;

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

测试一下数据库是否正常吧!

@SpringBootTest
class SpringBootShiroApplicationTests {
    @Autowired
    UserServiceImpl userService;

    @Test
    void contextLoads() {
        User user = userService.queryUserByName("admin");
        System.out.println(user);
        // User(id=3, username=admin, password=123456)
    }
}

改造UserRealm,连接到数据库进行真实的操作!

public class UserRealm extends AuthorizingRealm {
    @Autowired
    UserService userService;

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

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        // 用户名认证
        User user = userService.queryUserByName(userToken.getUsername());
        if (user == null) {
            // 用户名不存在
            // shiro底层就会抛出 UnknownAccountException异常
            return null;
        }
        /*
            密码认证
            验证密码,我们可以使用一个AuthenticationInfo实现类SimpleAuthenticationInfo
            shiro会自动帮我们验证!重点是第二个参数就是要验证的密码!
         */
        return new SimpleAuthenticationInfo("", user.getPassword(), "");
    }
}

重启项目测试成功!


4.用户授权操作

使用shiro的过滤器来拦截请求即可!

在 ShiroFilterFactoryBean 中添加一个过滤器,赋予权限访问机制:

// 授权过滤器
filterMap.put("/user/add","perms[user:admin]"); // 进入add页面需要有admin的权限!

我们再次启动测试一下,访问add,发现以下错误!未授权错误!

在这里插入图片描述

注意:当我们实现权限拦截后,shiro会自动跳转到未授权的页面,但我们没有这个页面,所以401 了

配置一个未授权的提示的页面,增加一个controller提示:

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

然后在 shiroFilterFactoryBean 中配置一个未授权的请求页面!

// 配置未授权的跳转页面
subject.setUnauthorizedUrl("/noauth");

测试启动,成功!

在这里插入图片描述


5.Shiro授权

想要实现不同用户的权限访问,需要在数据库中新增perms字段,标识每一个用户的权限:

例如:

在这里插入图片描述

之后对应更新pojo类!

在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;
}

之后注意,认证的返回值的第一个参数要为user:

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

授权成功!


6.Shiro整合Thymeleaf

我们想根据权限展示不同的前端页面,没有对应权限的用户登录不显示前端内容,这就需要实现一个Shiro整合Thymeleaf:

添加Maven的依赖:

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

配置一个shiro的Dialect ,在shiro的配置中增加一个Bean:

/**
 * 整合Thymeleaf用到的bean
 * @return
 */
@Bean
public ShiroDialect getShiroDialect(){
	return new ShiroDialect();
}

修改前端的配置:

导入命名空间:

xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"

修改index.html的代码:

<div shiro:notAuthenticated>
    <a th:href="@{/toLogin}">登录</a>
</div>
<div shiro:hasPermission="user:admin">
    <a th:href="@{/user/add}">add</a>
</div>
<div>
    <a th:href="@{/user/update}">update</a>
</div>

重启项目测试成功!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/98800.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《美国职业橄榄球大联盟》:NFL·橄榄1号位

基本装备 NFL橄榄球是一项过程极为激烈的比赛&#xff0c;阻挡、拦截与冲撞都是比赛不可或缺的一部分&#xff0c;这也可以说是橄榄球的一大特色。为了保护球员的安全&#xff0c;避免因为球员受伤而耽误球赛&#xff0c; NFL与NCAA都要求所有球员必须“穿戴合适且合法的护具”…

IfcOpenShell正确设置几何体的坐标

在之前的文章中&#xff0c;我们使用 IfcOpenShell (IOS) 读取 ifc 几何并将其转换为 brep。 当我们读取 wikilab.ifc文件时&#xff0c;一切似乎都是正确的&#xff0c;但真的如此吗&#xff1f; 当你在项目中使用 BIM 时&#xff0c;坐标始终是正确讨论的主题。 就此而言&am…

Android开发如何使用Docker为Jenkins持续集成助力

Android开发如何使用Docker为持续集成助力 为什么使用Docker 我为啥要使用到Docker呢&#xff1f;其实也是被动的&#xff0c;因为公司的项目托管在Coding上面&#xff0c;然后Jenkins集成也用的是Coding的&#xff0c;Coding默认提供了Android-29&#xff0c;JDK-8的构建环境…

【JAVA进阶】多态,内部类

&#x1f4c3;个人主页&#xff1a;个人主页 &#x1f525;系列专栏&#xff1a;JAVASE基础 目录 一、多态 1.多态的概述 2.多态的优势 3.类型转换问题 二、内部类 1.内部类概述[了解] 2.静态内部类[了解] 3.成员内部类[了解] 4.匿名内部类概述[重点] 一、多态 1.多态…

树状数组经典例题

目录 1.数星星 2.小朋友排队 3.求逆序对 1.数星星 题目描述 天空中有一些星星,这些星星都是在不同的位置,每个星星都有一个坐标。 如果一个星星的左下方(包含正左和正下)有k颗星星,就说这颗星星是k级的。 例如,上图中星星5是3级的(1,2,4在它的左下),星星2,4是1级的。…

idea中推送本地仓库和远程仓库后代码回退

本地仓库代码提交后回退 提交到本地仓库后 点击提交后会保存在本地仓库 本地仓库的回撤 找到git的提交记录 右键选择撤销还原 撤销还原后会出现提交文件&#xff0c;成功将本地仓库的文件移除&#xff0c;但是本地的错误代码仍然存在 如果想撤销提交到本地仓库的错误文…

SOLID 设计原则 - 这篇最容易消化

面向对象设计原则 SOLID 应该是职业程序员必须掌握的基本原则&#xff0c;每个程序员都应该了然于胸&#xff0c;遵守这 5 个原则可以帮助我们写出易维护、易拓展的高内聚低耦合的代码。 它是由罗伯特C马丁(知名的 Rob 大叔)21世纪初期 (准确来说&#xff0c;2000年在他的论文…

DevOps实战系列【第十章】:详解Jenkins Pipeline基本概念和语法

个人亲自录制全套DevOps系列实战教程 &#xff1a;手把手教你玩转DevOps全栈技术 流水线基本概念 官方中文手册&#xff1a; https://www.jenkins.io/zh/doc/book/pipeline 我们最好在结合英文文档去看&#xff0c;因为翻译过来的中文比较乱。 Jenkins pipeline是一套插件&…

MySQL 日志,难怪被模仿

一.前言 日志是mysql数据库的重要组成部分&#xff0c;记录着数据库运行期间各种状态信息。mysql日志主要包括错误日志、查询日志、慢查询日志、事务日志、二进制日志几大类。 通过分析日志&#xff0c;我们可以优化数据库性能&#xff0c;排除故障&#xff0c;甚至能够还原数…

中文文献检索网站

1. 中国知网 网址&#xff1a; https://www.cnki.net/ 中国知网被称众多科研人称国内文献论文最全最权威的中文文献库&#xff0c;知网提供中国学术文献、外文文献、学位论文、报纸、会议、年鉴、工具书等各类资源统一检索、统一导航、在线阅读和下载服务。 2. 掌桥科研 网址…

25.Django大型电商项目之地址管理——如何使用三级联动菜单数据加载地址、保存数据、动态获取数据、设置默认值

1. 地址管理基本页面 1.1 概述 1.2 流程 修改templates的跳转链接center.html <ul><li><a href"/userapp/address/">地址管理</a></li> </ul>templates {% extends base.html %} {% block title %}用户中心{% endblock %} {…

东北大学数据结构第八周(排序)

7-1 快速排序 作者 朱允刚 单位 吉林大学 给定包含n个元素的整型数组a[1],a[2],…,a[n]&#xff0c;利用快速排序算法对其进行递增排序&#xff0c;请输出排序过程&#xff0c;即每次Partition之后的数组。每次选择所处理的子数组的第一个元素作为基准元素。 输入格式: 输入为…

MapReduce 序列化案例

文章目录MapReduce 序列化案例一、案例需求二、案例分析map 阶段Reduce 阶段三、代码实现1、编写流量统计的Bean对象2、Mapper阶段代码MapReduce 序列化案例 一、案例需求 1、需求&#xff1a; 统计每一个手机号耗费的总上行流量&#xff0c;下行流量&#xff0c;总流量 2、输…

二、ZFNet可视化卷积神经网络——可解释性机器学习(DataWhale组队学习)

目录引言ZFNet的网络结构可视化反卷积反池化反激活反卷积训练细节特征可视化特征演化特征不变性局部遮挡测试敏感性分析相关性分析消融实验宽度影响深度影响迁移学习能力有效性分析总结引言 纽约大学ZFNet&#xff0c;2013年ImageNet图像分类竞赛冠军模型。对AlexNet进行改进的…

AOP 操作

AOP 操作AOP 操作&#xff08;准备&#xff09;1. Spring 框架一般是基于 AspectJ 实现 AOP 操作&#xff08;1&#xff09;什么是 AspectJ2. 基于 AspectJ 实现 AOP 操作3. 在项目工程里面引入 AOP 先关的依赖4. 切入点表达式举例1&#xff1a;对 com.fairykunkun.dao.UserDao…

谈谈自己对依赖注入的理解

1. 絮絮叨叨 1.1 想学习Google Guice 在工作的过程中&#xff0c;发现有名的大数据组件Presto大量使用Google Guice实现各种Module的构建 很多bind(interface).to(implementClass).in(scope)语句&#xff0c;实现接口与实现类的绑定&#xff0c;并指定实现类是单例还是多例 /…

Service的绑定过程

前言 Service的绑定过程将分为两个部分来进行讲解&#xff1b;分别是Contextlmpl到AMS的调用过程和Service的绑定过程。 frameworks/base/core/java/android/content/ContextWrapper.javapublic boolean bindService(Intent service, ServiceConnection conn,int flags) {ret…

计算机毕设Python+Vue-新型冠状病毒防控咨询网站(程序+LW+部署)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

根据端口划分虚拟局域、集线器、中继器、交换机、路由器、网桥----计算机网络

集线器&#xff1a; 连接计算机和交换机&#xff0c;类似于多台中继器。 实现多台电脑的同时使用一个进线接口来上网或组成局域网 中继器&#xff1a; 连接两条电缆&#xff0c;作用是放大前一条电缆里面的信号并传入下一条电缆。 是对接收到的信息进行再生放大&#xff0c;以…

Jenkins + Jmeter + Ant 持续集成

搭建提前安装好&#xff1a;ant Jenkins 环境 一、Jenkins 安装 Ant 插件&#xff1a; 进入Jenkins 配置插件页面&#xff0c;安装ant 插件&#xff1a; 打开插件配置页面&#xff0c;如下图&#xff1a; 点击“Available” 在输入框搜索 ant 安装即可&#xff1a; 二、安装…