Java研学-Shiro安全框架(四)

news2025/3/3 18:32:59

六 SpringBoot集成Shiro认证

1 分析

  Shiro提供认证授权功能,所以SpringBoot中不需再编写自定义注解,权限拦截,登录拦截,登录登出。Shiro 环境中有三个封装对象Subject ,SecurityManager和Realms,SpringBoot 集成 Shiro 时需要配置相对应的Bean(Subject 不用)
shiro认证

2 导入依赖

<properties>
	<java.version>8</java.version>
	<shiro.version>1.7.1</shiro.version>
	<thymeleaf.extras.shiro.version>2.0.0</thymeleaf.extras.shiro.version>
</properties>
<!--Shiro核心框架 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>${shiro.version}</version>
</dependency>
<!-- Shiro使用Spring框架 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>${shiro.version}</version>
</dependency>
<!-- Shiro使用EhCache缓存框架 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
    <version>${shiro.version}</version>
</dependency>
<!-- thymeleaf模板引擎和shiro框架的整合 -->
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>${thymeleaf.extras.shiro.version}</version>
</dependency>

3 创建数据源

// 有类才能生成Bean
public class EmployeeRealm extends AuthorizingRealm {
    @Autowired
    private IEmployeeService employeeService;
    @Autowired
    private IPermissionService permissionService;
    @Autowired
    private IRoleService roleService;
    //授权方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        Employee currentEmployee= (Employee) principalCollection.getPrimaryPrincipal();
        SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
        if(currentEmployee.isAdmin()){
            List<Role> roles=roleService.listAll();
            for(Role role:roles){
                info.addRole(role.getSn());
            }
            info.addStringPermission("*:*");
        }else{
            List<Role> roleList=roleService.queryByEmployeeId(currentEmployee.getId());
            for(Role role:roleList){
                info.addRole(role.getSn());
            }
            //查询该用户的权限集合
            List<String> permissionList=permissionService.queryByEmployeeId(currentEmployee.getId());
            info.addStringPermissions(permissionList);
        }
        return info;
    }
     //认证方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 根据token获取用户名
        String username = (String) authenticationToken.getPrincipal();
        // 根据用户名查询用户
        Employee currentEmployee=employeeService.getByUsername(username);
        // 根据查询结果返回对应数据
        if(currentEmployee==null){
            return null;
        }
        return new SimpleAuthenticationInfo(currentEmployee,currentEmployee.getPassword()
                , ByteSource.Util.bytes(currentEmployee.getSalt()),getName());
    }
}

4 创建Shiro配置类

// 配置类注解
@Configuration
public class ShiroConfig {
    // 1.Realm 数据源从数据库中查询数据(先有Realm才能配置Bean,配置这个Bean需要先有这个类)
    // Bean一定是对象,对象不一定是Bean,对象需要基于类创建
    @Bean
    public EmployeeRealm employeeRealm(){
        EmployeeRealm realm = new EmployeeRealm();
        return realm;
    }
    // 2.SecurityManager 安全管理器(基于web环境下的)
    // 此处可用set调本类方法或传参的方式联系Realm    
    // 传参是在spring容器中查找这个Bean先类型再名字,去掉@Bean注解会报错(参数名与方法名尽量一致)
    // 调用方法首先不会运行该方法,会看方法的返回值类型,在容器中查找该类型,找到多个再按照名字去找,找到了就直接用
    // 不会运行该方法,若在容器中没找到该方法,就运行该方法并把返回值放到容器中,然后再拿过来用(无@Bean注解也行)
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(EmployeeRealm employeeRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(employeeRealm);
        return securityManager;
    }
    // 请求拦截器 shiro过滤器 由于创建麻烦此处使用工厂类创建过滤器对象
    // 若想知道一个工厂类返回什么类型的Bean 可查询其getObject()方法
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
        // 配置登录页面
        shiroFilterFactoryBean.setLoginUrl("/static/login.html");
        // 配置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 配置拦截规则(过滤链) 底层为双链表组成(有序,就是我们放入的顺序,过滤器根据顺序执行)
        LinkedHashMap<String,String> filterChainDefinitionMap=new LinkedHashMap<>();
        // 对静态资源设置匿名访问(浏览器图标 html css js)放行
        filterChainDefinitionMap.put("/favicon.ico**","anon");
        filterChainDefinitionMap.put("/static/**","anon");
        // 不需拦截的访问(公共资源)放行
        filterChainDefinitionMap.put("/login","anon");
        // 退出并且shiro清除session信息(上下文对象也就是用户信息) 执行退出方法
        // 无需在再编写退出方法,直接调用logout即可踢回登陆页面
        filterChainDefinitionMap.put("/logout","logout");
        // 进行拦截
        filterChainDefinitionMap.put("/**","authc");
        // 将拦截规则设置给拦截器链(shiro生成了很多拦截器,看我们选用哪个)
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        // 此时无登录信息 访问任何页面都应该被踢回登录页面
        return shiroFilterFactoryBean;
    }
}

5 LoginController – 登录方法

  登录认证实际上是shiro在做,但数据需要在realm中提供

@Controller
public class LoginController {
    @RequestMapping("/login")
    @ResponseBody
    public JsonResult login(String username, String password){
        // 他会自动从Spring容器中拿到SecurityManager设置给SecurityUtils
        // 然后再将SecurityManager设置给subject
        Subject subject = SecurityUtils.getSubject();
        // 将用户名密码封装到token
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);
        // 此处返回异常不精确到某一项,防止有人试错(先试帐号再试密码)
        try {
            // 登录失败就抛异常
            subject.login(token);
            subject.getSession().setAttribute("user_in_session",subject.getPrincipal());
        } catch (UnknownAccountException e) {
            return new JsonResult(false,"账号密码有误");
        } catch (IncorrectCredentialsException e) {
            return new JsonResult(false,"帐号密码有误");
        } catch (Exception e) {
            return new JsonResult(false,"系统异常,稍后再试");
        }
        return new JsonResult(true,"登录成功");
    }
}

6 数据源查询方法(service) – getByUsername

// IEmployeeService
Employee getByUsername(String username);
// EmployeeServiceImpl
public Employee getByUsername(String username) {
	return employeeMapper.getByUsername(username);
}

7 数据源查询方法(mapper) – getByUsername

// mapper
Employee getByUsername(String username);
// xml
<select id="getByUsername" resultMap="BaseResultMap">
      select e.id, e.username, e.name, e.password, e.email, e.age, e.admin,d.id d_id,d.name d_name,d.sn d_sn,e.salt
      from employee e left join department d on e.dept_id = d.id
      where username=#{username}
</select>

8 shiro 内置过滤器

  shiro 启动时会默认将以下这些类(过滤器)加载到程序中,然后使用Map将这些数据的关系以key value的方式存储起来。

过滤器的名称(key)Java 类(value)
anonorg.apache.shiro.web. lter.authc.AnonymousFilter
authcorg.apache.shiro.web. lter.authc.FormAuthenticationFilter
authcBasicorg.apache.shiro.web. lter.authc.BasicHttpAuthenticationFilter
rolesorg.apache.shiro.web. lter.authz.RolesAuthorizationFilter
permsorg.apache.shiro.web. lter.authz.PermissionsAuthorizationFilter
userorg.apache.shiro.web. lter.authc.UserFilter
logoutorg.apache.shiro.web. lter.authc.LogoutFilter
portorg.apache.shiro.web. lter.authz.PortFilter
restorg.apache.shiro.web. lter.authz.HttpMethodPermissionFilter
sslorg.apache.shiro.web. lter.authz.SslFilter

anon: 匿名拦截器,即不需要登录即可访问(谁都可以访问,不需要拦截);一般用于静态资源过滤;示例“/static/**=anon”

authc: 表示需要认证(登录)才能使用,该路径所有请求都需登录后才能访问;示例“/**=authc”

authcBasic:Basic HTTP身份验证拦截器

roles: 角色授权拦截器,验证用户是否拥有资源角色;示例“/admin/**=roles[admin]”

perms: 权限授权拦截器,验证用户是否拥有资源权限;示例“/user/create=perms[“user:create”]”

user: 用户拦截器,用户已经身份验证/记住我登录的都可;示例“/index=user”

logout: 退出拦截器,登出后自动清理session中的用户信息。主要属性:redirectUrl:退出成功后重定向的地址(/);示例“/logout=logout”

port: 端口拦截器,主要属性:port(80):可以通过的端口;示例“/test= port[80]”,如果用户访问该页面是非80,将自动将请求端口改为80并重定向到该80端口,其他路径/参数等都一样

rest: rest风格拦截器;

ssl: SSL拦截器,只有请求协议是https才能通过;否则自动跳转会https端口(443);其他和port拦截器一样;

9 400错误问题解决

  当传入的参数 SpringMVC 无法转换时,就会出现400问题(第一次访问时出现),session是在浏览器第一次访问服务器时,由服务器创建并生成一个sessionID,通过response响应给浏览器。

  浏览器访问服务器时,服务器中有一个session池,当找到session后,将返回对应的JsessionID。

  但第一次访问时会首先经过shiro过滤器,其中有一个会话管理器(SessionManager),他发现当前是第一次访问,因此会进行一次URL重写,服务器会生成session并把sessionID返回给安全管理器,会话管理器通过重定向回到浏览器,再次发起申请访问服务器,此时第一次访问服务器实际上是没有访问到服务器,因此服务器无法接收携带的参数(JsessionID)报400错误。(去掉url中的JsessionID即可访问)

  第二次访问时,session已经存在,通过id寻找session,可以正常访问。或者告诉会话管理器,不需要做url重写,可在shiroconfig中生成会话管理器,将 setSessionIdUrlRewritingEnabled 设置为false即可(默认为true)。

@Configuration
public class ShiroConfig {
    // 略
    // 会话管理器
    @Bean
    public DefaultWebSecurityManager sessionManager(){
        DefaultWebSessionManager sessionManager=new DefaultWebSessionManager();
        // url重写开关
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }
}

  此处需注意,编写好 sessionManager 会话管理器后,需要将其设置给 SecurityManager 安全管理器(通过参数)

@Configuration
public class ShiroConfig {
	// 略
    @Bean
    //安全管理器
    public DefaultWebSecurityManager defaultWebSecurityManager(EmployeeRealm employeeRealm, DefaultWebSessionManager sessionManager){
        // 创建安全管理器
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        // 设置realm
        securityManager.setRealm(employeeRealm);
        // 设置会话管理器
        securityManager.setSessionManager(sessionManager);
        return securityManager;
    }
    //会话管理器
    @Bean
    public DefaultWebSecurityManager sessionManager(){
        DefaultWebSessionManager sessionManager=new DefaultWebSessionManager();
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }
}

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

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

相关文章

hcip暑假第二次作业

ip配置如下 配置缺省路由 [R1]ip route-static 0.0.0.0 0 12.0.0.2 -------设置缺省路由 [R1]ip route-static 0.0.0.0 0 21.0.0.2 [R2]ip route-static 0.0.0.0 0 22.0.0.2 [R3]ip route-static 0.0.0.0 0 23.0.0.2 [R4]ip route-static 0.0.0.0 0 24.0.0.2 [R5]ip route…

python如何计算两个时间相差多少秒钟,分钟,小时,天,月,年

使用场景&#xff1a;在做上课记录系统的时候&#xff0c;有上课开始时间和上课结束时间&#xff0c;需要计算这两个时间的插值&#xff0c;以分钟为单位。 封装方法如下&#xff1a; from datetime import datetimedef sub_seconds(date1: str "2024-07-11 12:33:33&q…

编程范式之事件驱动编程

目录 前言1. 定义2. 特点2.1 异步性2.2 解耦2.3 可扩展性2.4 高度响应 3. 适用场景3.1 用户界面开发3.2 网络编程3.3 游戏开发3.4 物联网&#xff08;IoT&#xff09; 4. 优点4.1 提高效率4.2 灵活性和可扩展性4.3 解耦和模块化4.4 高响应性 5. 缺点5.1 复杂性增加5.2 调试困难…

Spring中如何操作Redis

Spring毕竟是Java中的一个主流框架&#xff0c;如何在这个框架中使用Redis呢&#xff1f; 创建项目并引入相关依赖 然后进行创建。 至此就将Redis的相关依赖引入进来了。 编写Redis配置 将application.properties修改成application.yml 然后编写如下配置&#xff1a; spr…

昇思学习打卡-16-热门LLM及其他AI应用/K近邻算法实现红酒聚类

文章目录 算法原理距离定义模型构建 算法原理 K近邻算法可以用在分类问题和回归问题上&#xff0c;它的原理如下&#xff1a;要确定一个样本的类别&#xff0c;可以计算它与所有训练样本的距离&#xff0c;然后找出和该样本最接近的k个样本&#xff0c;统计出这些样本的类别并…

青否数字人手机端APP抖音开播完整教程!

1.开播--所需硬件 安卓手机设备--支持 直播手机推荐型号&#xff1a;配备了麒麟980,骁龙865,天玑1000以上的处理器的安卓手机&#xff0c;运营内存不低于6G 网络宽带&#xff1a;上下行宽带稳定在10Mbps以上&#xff08;普通家用1000M宽带的上行速度大约为30Mbps&#xff0c;最…

二分查找模板及例题

文章目录 模板一&#xff1a;使用场景&#xff1a;解释&#xff1a;例题&#xff1a;数的范围题意&#xff1a; 代码&#xff1a; 模板二&#xff1a;使用场景&#xff1a;解释&#xff1a;例题&#xff1a;[Building an Aquarium](https://codeforces.com/problemset/problem/…

Tita的OKR:最新20个HR人力资源OKR案例

OKR是一个目标设定框架&#xff0c;可以提高员工的参与度&#xff0c;同时帮助人们专注于最重要的事情。 然而&#xff0c;OKR最大的挑战之一是设定正确的目标&#xff0c;我与很多人力资源专业人士交谈过&#xff0c;他们证明他们的OKR并不完美。 这就是为什么我们收集了最佳…

【2024_CUMCM】机器学习导论、不平衡分类模型(重采样)、交叉验证(附代码)

目录 基本概念 机器学习概念与过程 机器学习的分类 学习任务 分类与回归区分 学习方式 不平衡分类模型 什么是不平衡分类 下采样的优点和缺点 过采样的优点和缺点 交叉验证 what 过拟合 常见方法 K折交叉验证&#xff08;K-Fold Cross Validation&#xff09; …

后VMware时代,一体化技术平台建设思路

在数字化转型的浪潮中&#xff0c;企业对IT基础设施的需求正在发生根本性的变化。VMware时代的结束&#xff0c;为企业带来了重新构建技术平台的机遇与挑战。6月28日&#xff0c;在主题为【聚力生态&#xff0c;VMware全链替代】的线上研讨会上&#xff0c;灵雀云首席解决方案专…

适合运动的骨传导耳机有哪些?总结五款适合运动的骨传导耳机推荐

在追求健康生活的浪潮中&#xff0c;运动健身蔚然成风&#xff0c;而运动时的音乐陪伴更是不可或缺。然而&#xff0c;传统耳机在运动场景下的种种不便&#xff0c;促使市场寻找更佳解决方案。此时&#xff0c;骨传导耳机应运而生&#xff0c;以其独特的传音方式&#xff0c;为…

ESP32-WROVER-E/ESP32-WROVER-IE模组硬件相关

ESP32-WROVER-E和ESP32-WROVER-IE区别 带I的为采用外部天线&#xff0c;不带I的使用PCB天线。 模组后缀N16R8&#xff0c;16为4、8、16为FLASH存储器容量&#xff0c;8为8或2&#xff0c;为PSRAM的容量&#xff0c;单位均为MB。 模组芯片 ESP32-WROVER-E 和 ESP32-WROVER-IE…

tkinter-TinUI-xml实战(11)多功能TinUIxml编辑器

引言 在TinUIXml简易编辑器中&#xff0c;我们通过TinUI搭建了一个简易的针对TinUIXml布局的编辑器&#xff0c;基本掌握了TinUIXml布局和TinUIXml的导入与导出。现在&#xff0c;就在此基础上&#xff0c;对编辑器进行升级。 本次升级的功能&#xff1a; 更合理的xml编辑与…

【洞见】智算操作系统,推进算力迈入普惠智算新时代

WAIC 2024世界人工智能大会暨人工智能全球治理高级别会议正式拉开帷幕&#xff0c;面向世界徐徐展示人工智能技术最前沿的理念和成果。九章云极DataCanvas公司董事长方磊受邀参加大会开幕式&#xff0c;并在“迈向AGI&#xff1a;大模型焕新与产业赋能”论坛发表主题演讲&#…

docker 上传镜像到hub仓库

要将 Docker 镜像上传到 Docker Hub&#xff0c;你需要按照以下步骤操作&#xff1a; 登录 Docker Hub 首先&#xff0c;你需要登录到 Docker Hub。打开终端并运行以下命令&#xff1a;docker login系统会提示你输入 Docker Hub 的用户名和密码。 如果密码忘记可以token登录&a…

函数式接口、匿名内部类、lambda表达式

一、函数式接口 只有一个抽象方法的接口叫函数式接口&#xff0c;不能有两个&#xff0c;也不能有方法实现。 FunctionalInterface注解标记&#xff0c;在idea中可以用这个注解验证是不是函数式接口。实现函数式接口可以转成lambda表达式。 二、匿名内部类 匿名内部类的格式&a…

[k8s源码]1.client-go集群外部署

client-go是由k8s发布且维护的专门用于开发者和kubernetes交互的客户端库。它支持对k8s资源的CRUD操作&#xff08;create、read、update、delete&#xff09;&#xff0c;事件监听和处理&#xff0c;访问kubernetes集群的上下文和配置。 client go是独立于kubernetes集群之外…

爬虫如此简单?一文教会网络爬虫!

在今天的网络环境中&#xff0c;数据的获取对于许多电商和信息分析人员来说至关重要。但是频繁的数据采集行为可能会导致目标网站对你的IP进行封锁或限制访问频率。这时候&#xff0c;使用代理IP可以是一个有效的解决方案。本文将向你介绍如何使用代理IP进行网络爬虫&#xff0…

《计算机研究与发展》投稿经验

前言 记录下投计研展正刊的经验。跟专刊不同哈&#xff0c;专刊会更快&#xff0c;我看好像也有专刊的经验分享。【计算机研究与发展投稿记录】 期刊简介 来源计研展官网&#xff1a; 《计算机研究与发展》是中国科学院计算技术研究所和中国计算机学会联合主办的学术性期刊&a…

sqlmap确定目标/实操

安装kali&#xff0c;kali自带sqlmap&#xff0c;在window系统中跟linux系统操作有区别 sqlmap是一款自动化SQL工具&#xff0c;打开kali终端&#xff0c;输入sqlmap&#xff0c;出现以下界面&#xff0c;就说明sqlmap可用。 sqlmap确定目标 一、sqlmap直连数据库 1、直连数据库…