springboot-shiro

Shiro

根据狂神课程学习

1.1、什么是Shiro?

  • Apache Shiro是一个强大且易用的Java安全框架
  • 执行身份验证、授权、密码和会话管理

为什么要使用shiro?

如果你是需要设计RBAC(Role Based Access Control)基础系统,需要编写大量用于权限控制的代码时。那么你需要使用Shiro。因为Shiro已经将RBAC系统大量的代码封装好,可以减少我们大量的工作量。

如:页面的显示的HTML控件根据登录用户的权限不同而不同。使用Shiro可以轻松解决。

有哪些功能?

在这里插入图片描述

Shiro整体架构

在这里插入图片描述

shiro

1.Authenticator:认证器,管理登陆与登出。

2.Authorizer:授权器,赋予主体权限。

3.Session Manager:session管理器,session管理机制。不借助任何web容器使用session

4.Session Dao:session操作,主要增删改查。

5.Cache Manager:缓存管理器

6.Pluggable Realms(1 or more):shiro与数据库的连接,认证授权校验

7.Cryptography:数据加密,加密算法的实现(SHA、MD5)

8.web Support:对Web项目的支持,Shiro的标签!!

访问流程图

在这里插入图片描述

  1. 首先应用访问(可以使用远程调用,可以是Web请求等),Shiro通过一个Subject对象来标识当前访问的身份。这句话告诉我们,第一次访问的时候,Shiro肯定会创建一个Subject对象标签当前请求(用户)的身份。
  2. SecurityManger容器创建一个Subject对象验证请求的参数,SecurityManager的作用是统一管理Subject。这句话意味着,一个SecurityManager对象管理多个Subject的对象。
  3. Subject通过SecurityManger获得操作当前用户的权限,在启动的那一刻,SecurityManger就会加载shiro.ini权限配置文件,在用户登录成功后,可以根据shiro配置的信息,获得用户对应的权限。
  4. shiro配置:是一个权限控制信息文件,里面必须包括用户的验证信息,权限的信息

快速开始

https://github.com/apache/shiro/blob/master/samples/quickstart/

1.导入日志相关依赖

2.配置文件 log4j.properties

3.快速开始,helloworld

快速开始分析:

获取当前用户对象

Subject currentUser = SecurityUtils.getSubject();

通过当前用户拿到session

Session session = currentUser.getSession();

用户对象的常用方法:

	//判断当前用户是否被认证
	currentUser.isAuthenticated()
	//获得当前用户的一个认证
	currentUser.getPrincipal()
	//当前用户是否拥有xx角色
	currentUser.hasRole("schwartz")
    //已登录用户是否具有某种权限
    currentUser.isPermitted("lightsaber:wield")

令牌:

UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
token.setRememberMe(true);//设置记住我功能
currentUser.login(token);//执行登录操作
currentUser.logout();//退出操作

springboot整合shiro

1.先新建一个spring boot的web项目,引入thymeleaf依赖,写好controller,搭建好基础环境,确认能跑起来

2.导入jar包

<!--shiro整合spring的包-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.1</version>
        </dependency>

3.编写配置类

config->ShiroConfig.java

package cn.fzj.config;

import org.springframework.context.annotation.Configuration;

/**
 * @author Mike
 */
@Configuration
public class ShiroConfig {
    //创建realm对象,需要自定义类
    //DefaultWebSecurityManager
    //shiroFilterFactoryBean
  
}

config->UserRealm 继承AuthorizingRealm

用户认证:

package cn.fzj.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;

/**
 * @author Mike
 * 自定义的realm
 */
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("执行了=>认证doGetAuthorizationInfo");
        return null;
    }
}

完善ShiroConfig.java

import org.springframework.context.annotation.Configuration;

/**
 * @author Mike
 */
@Configuration
public class ShiroConfig {
    //1.创建realm对象,需要自定义类
    @Bean(name = "userRealm")
    public UserRealm userRealm(){
        return new UserRealm();
    }
    //2.DefaultWebSecurityManager
    @Bean(name="securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联UserRealm
        securityManager.setRealm(userRealm);
        return securityManager;
    }
    //3.shiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);
        return bean;
    }
 
}

登录拦截

写两个页面,页面跳转的请求,index写两个a标签

@RequestMapping("/user/add")
    public String add(){
        return "user/add";
    }
    @RequestMapping("/user/update")
    public String update(){
        return "user/update";
    }

修改ShiroConfig.java->getShiroFilterFactoryBean()做测试

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

此时点击页面发现点add会报错,点update可以正常跳转

我们并不像让他报错,想让他跳转到一个登录页面怎么办呢?

这会新建一个login.html

修改ShiroConfig.java->getShiroFilterFactoryBean(),添加这样一句代码

//设置登陆的请求
bean.setLoginUrl("/toLogin");

此时点击权限不够的a标签就会跳转到登录页面

那么我们怎么给一个用户给权限呢?也就是

用户认证

springboot整合shiro,mybatis

1.导包

2.application.yml

3.application.properties

4.pojo

5.mapper->UserMapper(interface)

6.UserMapper.xml

7.UserService

8.UserServiceImpl

9.测试一下,底层能不能成功运行

登录请求

 @RequestMapping("/toLogin")
    public String toLogin(){
        return "login";
    }
@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);//执行登录方法

            return "index";
        }
        catch(UnknownAccountException e){
            model.addAttribute("msg","用户名错误");
            return "login";
        }catch (IncorrectCredentialsException e){
            model.addAttribute("msg","密码错误");
            return "login";
        }

    }
@RequestMapping("/noAuth")
    @ResponseBody
    public String unAuthorized(){
        return "未经授权无法访问此页面";
    }

认证

连接真实的数据库

修改UserRealm.java

 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行了=>授权doGetAuthorizationInfo");
        
        UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
        //认证用户名,密码,数据库中取
        User user = userService.queryUserByName(userToken.getUsername());
        if (user==null){//用户为空,没找到
            return null;//会抛出一个异常
        }
        //关于密码的认证由shiro自己做
        return new SimpleAuthenticationInfo(user,user.getPwd(),"");
    }//这里传回user对象,后面在授权时才能通过subject.getPrincipal()拿到user对象

shiroConfig.java

public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);
        //添加shiro的内置过滤器
        /*
            anon:无需认证就可以访问
            authc:必须认证了才能访问
            user:必须拥有 记住我功能才能用
            perms:拥有对某个资源的权限才能访问
            role:拥有某个角色权限才能访问
         */
        Map<String,String> filterMap = new LinkedHashMap<>();
        //授权,然后用户点击了未授权的请求,会跳转到充值页面
        filterMap.put("/user/add","perms[user:add]");
    
        filterMap.put("/user/*","authc");
        bean.setFilterChainDefinitionMap(filterMap);
        //设置登陆的请求
        bean.setLoginUrl("/toLogin");
        //设置未授权页面
        bean.setUnauthorizedUrl("/noAuth");
        return bean;
    }

留意put的顺序

授权

修改UserRealm.java

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了=>授权doGetAuthorizationInfo");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermission("user:add");
        return info;
    }

但这样是每有一个用户经过就会赋给他权限,也就是说只要用户登陆了就有权限,没有权限的分级

再次修改UserRealm.java

 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了=>授权doGetAuthorizationInfo");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        //info.addStringPermission("user:add");
        //拿到当前登录的这个对象
        Subject subject = SecurityUtils.getSubject();
        User currentUser = (User) subject.getPrincipal();//拿到user对象
        info.addStringPermission(currentUser.getPerms());//设置当前用户的权限
        return info;
    }

修改ShiroConfig.java

//授权,然后用户点击了未授权的请求,会跳转到充值页面
        filterMap.put("/user/add","perms[user:add]");
        filterMap.put("/user/update","perms[user:update]");
//相当于是确认用户有没有某种权限

接下来,我们想让他有什么权限,就在界面上显示什么权限,没有的权限就不显示

shiro整合Thymeleaf

1.导入一个整合包

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

2.再shiroConfig.java中整合

//整合ShiroDialect:用来整合shiro thymeleaf
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }

3.index

<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">add</a>
</div>

4.UserRealm.java

   Subject currentSubject = SecurityUtils.getSubject();
        Session session = currentSubject.getSession();
        session.setAttribute("loginUser",user);//这会登陆成功就存到session

5.index.html

<div th:if="${session.loginUser==null}">
	<a th:href="@{toLogin}">登录</a>
</div>

GitHub上有完整代码
GitHub仓库地址

热门文章

暂无图片
编程学习 ·

Nginx学习笔记

1.Nginx简介 Nginx 是一个高性能的HTTP和反向代理服务器,特点是占内存少,并发能力强。Nginx转为性能优化而开发,性能是器最重要的考量,实现上非常注重效率,能经受住高负载的考验,有报告表明能支持的高达50000个并发连接数。 百度,腾讯,网易,淘宝等都在使用Nginx. 2.反…
暂无图片
编程学习 ·

javaScript之ES6

ES6新增的内容 新增的let和constlet num1 = 10console.log(num1) //10const num2 = 10console.log(num2) //10let const声明变量和 var声明变量的区别:用let 和const 声明的变量不会进行预解析,只能先声明后使用 用let 和const 不能重复声明同一个变量 用let 和const声明 变…
暂无图片
编程学习 ·

好程序员web前端培训分享HTMLCSS学习笔记BFC

好程序员web前端培训分享HTMLCSS学习笔记BFC,BFC(Block formatting context)直译为“块级格式化上下文”。它是一个独立的渲染区域,只有Block-level box(块)参与, 它规定了内部的Block-level Box如何布局,并且与这个区域外部毫不相干。 BFC的布局规则 一、内部的Box会在垂…
暂无图片
编程学习 ·

命令模式

菜鸟教程中代理模式总结)1.定义:将请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令 的合适的对象,并把该命令传给相应的对象,该对象执行命令。 2.主要解决:行为请求者与行为实现者通常是一种紧耦合的关系,为了消除这种耦合关系 3.何时使用:命…
暂无图片
编程学习 ·

UE4中让某个UI位于窗口的最顶端

1.处于同一嵌套层级的UI 可以使用Set ZOrder 设置那个Widget位于屏幕的最前面2.创建一个user widget 叫做ui_umg,里面加上两个按钮3.创建另外一个widget 叫做ui_pic,里面加上一个image4.这步是重点,ui_umg中按钮点击的时候 使用create widget 生成一个ui_pic, 但是这个时…
暂无图片
中恒嘉业 ·

Heap Sort 讲解

Heap Sort sorts a group of unordered elements using the Heap data structure. The sorting algorithm using a Min Heap is as follows: Heapify all elements into a Min HeapRecord and delete the top elementPut to top element into an array T that stores all so
暂无图片
cgfy ·

8. 源码分析之ConsumeQueue

源码分析之ConsumeQueue 消息发送时数据在ConsumeQueue的落地 ​ 连续发送5条消息&#xff0c;消息是不定长&#xff0c;首先所有信息先放入 Commitlog中&#xff0c;每一条消息放入Commitlog的时候都需要上锁&#xff0c;确保顺序的写入。 ​ 当Commitlog写成功了之后。数据…
暂无图片
coreui ·

Heap Sort 讲解

Heap Sort sorts a group of unordered elements using the Heap data structure. The sorting algorithm using a Min Heap is as follows: Heapify all elements into a Min HeapRecord and delete the top elementPut to top element into an array T that stores all so
暂无图片
coreui ·

[react] 你觉得react上手快不快?它有哪些限制?

[react] 你觉得react上手快不快&#xff1f;它有哪些限制&#xff1f; 相对vue来说不快。 限制 需要学习JSX需要工程化的配置需要对原生JavaScript有相当的掌握react只是一个UI层面的库&#xff0c;像vue内置了动画处理、keep-alive等功能&#xff0c;react则需要去找第三方库…
暂无图片
未来博客 ·

Heap Sort 讲解

Heap Sort sorts a group of unordered elements using the Heap data structure. The sorting algorithm using a Min Heap is as follows: Heapify all elements into a Min HeapRecord and delete the top elementPut to top element into an array T that stores all so
暂无图片
未来博客 ·

[react] 你觉得react上手快不快?它有哪些限制?

[react] 你觉得react上手快不快&#xff1f;它有哪些限制&#xff1f; 相对vue来说不快。 限制 需要学习JSX需要工程化的配置需要对原生JavaScript有相当的掌握react只是一个UI层面的库&#xff0c;像vue内置了动画处理、keep-alive等功能&#xff0c;react则需要去找第三方库…
暂无图片
建站日记 ·

[react] 你觉得react上手快不快?它有哪些限制?

[react] 你觉得react上手快不快&#xff1f;它有哪些限制&#xff1f; 相对vue来说不快。 限制 需要学习JSX需要工程化的配置需要对原生JavaScript有相当的掌握react只是一个UI层面的库&#xff0c;像vue内置了动画处理、keep-alive等功能&#xff0c;react则需要去找第三方库…
暂无图片
建站日记 ·

STL Practice —— 【map (1)】

Description 给出学生姓名和分数&#xff0c;要求你输入姓名查询分数。 Input 输入包含T组测试数据。 开头是一个正整数T (0<T<10)&#xff0c;为测试数据数量。 对于每组测试数据&#xff0c;第一行是一个正整数N (0<N<100000)。 接下来有N行&#xff0c;每行包…
暂无图片
mfbz ·

AOV网是否存在回路-拓扑排序-C++

拓扑排序是对测试AOV网是否存在回路的方法&#xff01; 拓扑排序的过程中&#xff0c;由于需要查找所有以某顶点为尾的弧&#xff0c;即找到该顶点的所有出边&#xff0c;故图要采用邻接表的存储方式。但拓扑排序较邻接表的存储方式有一点不同&#xff0c;由于要查找入度为0的点…
暂无图片
mfbz ·

[react] 你觉得react上手快不快?它有哪些限制?

[react] 你觉得react上手快不快&#xff1f;它有哪些限制&#xff1f; 相对vue来说不快。 限制 需要学习JSX需要工程化的配置需要对原生JavaScript有相当的掌握react只是一个UI层面的库&#xff0c;像vue内置了动画处理、keep-alive等功能&#xff0c;react则需要去找第三方库…
暂无图片
珊珊日记 ·

AOV网是否存在回路-拓扑排序-C++

拓扑排序是对测试AOV网是否存在回路的方法&#xff01; 拓扑排序的过程中&#xff0c;由于需要查找所有以某顶点为尾的弧&#xff0c;即找到该顶点的所有出边&#xff0c;故图要采用邻接表的存储方式。但拓扑排序较邻接表的存储方式有一点不同&#xff0c;由于要查找入度为0的点…
暂无图片
珊珊日记 ·

8. 源码分析之ConsumeQueue

源码分析之ConsumeQueue 消息发送时数据在ConsumeQueue的落地 ​ 连续发送5条消息&#xff0c;消息是不定长&#xff0c;首先所有信息先放入 Commitlog中&#xff0c;每一条消息放入Commitlog的时候都需要上锁&#xff0c;确保顺序的写入。 ​ 当Commitlog写成功了之后。数据…