Shiro框架基础内容

今天内容

  • Shiro概述
  • Shiro环境搭建
  • Shiro完成用户认证
  • Shiro完成用户授权
  • 自定义Shiro过滤器

第一章 Shiro概述

1. 当前项目的问题

  1. 用户不用登录,就能直接通过url访问saas系统内部的页面 直接输入http://localhost:8080/system/module/list.do就能看到所有模块
  2. 使用低权限的管理员身份登录,也可以访问高权限的用户菜单 比如使用普通员工登录, 直接url就能访问到它本应访问不到的页面
  3. 页面上标签未做到权限控制 给一个员工分配了部门管理\查看权限, 但没分配新增权限, 但页面上依旧显示新增按钮
问题:
	1. 用户不登录就可以访问系统资源
	2. 用户登录之后可以访问到不属于自己的资源
	3. 页面标签没有做到权限限制

​ 上述功能的描述,可以使用过滤器拦截器完成,当然在实际开发者已经有组织把这些功能封装为框架,比如Shiro或者SpringSecurity,来简化权限的控制,我们今天使用shiro框架

2. Shiro简介

Apache Shiro是Java的一个安全框架。不仅功能强大,而且使用简单,它为开发人员提供一个直观而全面的认证,授权,加密及会话管理的解决方案。

 

官网地址:http://shiro.apache.org/

3. Shiro的功能

* Authentication(认证)
		用户登录,身份识别    ---      结果:不是系统用户不能访问系统
		
* Authorization(授权)
		限定用户可进行的操作  ----     结果:没权限不能访问资源

* Cryptography(加密)
		安全数据加密
		
* Session Manager(会话)
		用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中(类似于web的session)
		
* Web Integration
		web容器集成
		
* Integrations
		集成其他应用,比如spring、缓存

4. Shiro的架构(画)

 

Shiro框架基础内容_第1张图片
* Application Code(应用)
		我们自己编写的项目
		
* Subject(主体)
		它是一个工具类,负责用户和shiro框架交互
		
* SecurityManager(安全管理器)
		它是Shiro架构中最核心的组件,通过它可以协调其他组件完成用户认证和授权
		
* Realm(域)
		它定义了访问数据的方式,用来连接不同的数据源,如:数据库,配置文件等

明确:我们使用shiro主要要做两件事

  • 认证:认证指的是匹配用户名(邮箱)和密码,让平台认识你。

  • 授权:授权指的是当前认证的用户进入平台,能操作哪些页面。

     

    Shiro框架基础内容_第2张图片

5. Shiro过滤器

过滤器简称 对应的java类 描述
anon org.apache.shiro.web.filter.authc.AnonymousFilter 未认证访问
authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter 认证后访问
authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter httpBasic认证后访问
perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter 指定权限访问
port org.apache.shiro.web.filter.authz.PortFilter 指定端口访问
rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter 指定rest访问
roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter 指定角色访问
ssl org.apache.shiro.web.filter.authz.SslFilter ssl认证后访问
user org.apache.shiro.web.filter.authc.UserFilter 指定用户访问
logout org.apache.shiro.web.filter.authc.LogoutFilter 用户退出

6. Shiro标签

标签名称 标签条件(均是显示标签内容)
登录之后
不在登录状态时
用户在没有RememberMe时
用户在RememberMe时
在有abc或者123角色时
拥有角色abc
没有角色abc
拥有权限资源abc
没有abc权限资源
默认显示用户名称

第二章 Shiro环境搭建

1. 导入坐标(已完成)



    org.apache.shiro
    shiro-spring
    1.3.2



    org.apache.shiro
    shiro-core
    1.3.2

2. web.xml配置

在web.xml中添加如下过滤器,注意filter-name的值是shiroFilter



   shiroFilter
   org.springframework.web.filter.DelegatingFilterProxy
   
      targetFilterLifecycle
      true
   


   shiroFilter
   
   /*

3. Spring整合shiro

添加spring整合shiro的配置文件

applicationContext-shiro.xml




  
  
    
    
    
    
    
    
    
      
        /login.jsp = anon
        /css/** = anon
        /img/** = anon
        /plugins/** = anon
        /make/** = anon
        /login.do = anon
        /** = authc
      
    
  

  
  

  
  
    
  

  
  
    
  
  
  
    
  
  


4. 自定义Realm

在export_manager_web的com.itheima.web.realm包创建类SaasRealm

package com.itheima.web.realm;

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;

public class SaasRealm extends AuthorizingRealm {
	/**
	 * @description 用户授权
	 * @author mryhl
	 * @date 2020/10/11 16:15 No such property: code for class: Script1
	 * @return
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
		System.out.println("=====================授权=====================");
		return null;
	}

	/** 
	 * @description 用户认证
	 * @author mryhl
	 * @date 2020/10/11 16:14 No such property: code for class: Script1
	 * @return 
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
		System.out.println("=====================认证=====================");
		return null;
	}
}

5. shiro执行流程的说明

 

Shiro框架基础内容_第3张图片

第三章 Shiro用户认证(重点)

1. 传统方式和shiro方式完成登录的流程区别

 

Shiro框架基础内容_第4张图片

2. 代码实现

2.1 修改LoginController

/**
	 * @description 用户登陆认证
	 * @author mryhl
	 * @date 2020/10/11 16:25 No such property: code for class: Script1
	 * @return
	 */
	@RequestMapping("/login")
	public String login(String email,String password) {
		// 1. 封装email和password为Token
		AuthenticationToken authenticationToken = new UsernamePasswordToken(email, new Md5Hash(password, email, 2).toString());
		// 2. 获取subject,并且调用login方法
		// 通过SecurityUtils获取subject
		Subject subject = SecurityUtils.getSubject();
		/**
		 * @author mryhl
		 * 调用login方法, 传入token.
		 * 并对此进行捕获异常
		 */
		try {
			subject.login(authenticationToken);
			// 登陆成功
			User user = (User) subject.getPrincipal();
			// 通过则保存用户数据
			session.setAttribute("loginUser",user);
			//根据用户查询对应的权限
			List moduleList=userService.findModuleByUser(user);
			session.setAttribute("modules", moduleList);
			return "redirect:/home/main.do";

		} catch (Exception e) {			
			request.setAttribute("error", "用户名或者密码错误");
			return "forward:/login.jsp";
		}

	}

2.2 修改SaasReam

/**
	 * @description 用户认证
	 * @author mryhl
	 * @date 2020/10/11 16:56
	 * @return AuthenticationInfo 的实现类 SimpleAuthenticationInfo 简单认证信息
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
		// 进入方法标识
		System.out.println("=====================认证=====================");

		// 强制类型转换
		UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
		// 获取传入的email
		String email = usernamePasswordToken.getUsername();
		// 根据用户进行查询
		User user = userService.findByEmail(email);
		// 判断返回信息
		if (user==null) {
			// 返回空的简单身份认证信息,代表没有找到内容
			return new SimpleAuthenticationInfo();
		}else {
			/**
			 * @author mryhl
			 * SimpleAuthenticationInfo三个参数
			 * Object principal 主角---->user
			 * Object credentials  密码--->user.getPassword()
			 * String realmName 当前realm的名称--->this.getName()
			 */
			return new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
		}

	}

3. 再谈shiro认证代码的执行流程

 

Shiro框架基础内容_第5张图片

4. 用户退出

//退出
@RequestMapping(value = "/logout", name = "用户登出")
public String logout() {
    SecurityUtils.getSubject().logout();   //登出
    return "redirect:/login.jsp";
}

第四章 Shiro用户授权(重点)

1. 授权流程说明

shiro授权的意思就是: 当一个用户试图访问一个资源的时候, shiro会判断此用户是否有访问此资源的权限

shiro授权的步骤:

  1. 定义每个资源访问所需要的权限
  2. 当用户访问资源的时候, 要去数据库查询到此用户拥有的权限
  3. 使用用户查询到的权限列表 跟 访问资源需要的权限比对, 决定授权是否通过

 

Shiro框架基础内容_第6张图片

2. 授权代码实现(xml版)

2.1 xml中定义权限

applicationContext-shiro.xml


/company/list.do = perms["企业管理"]

2.2 SaasRealm中实现授权逻辑

/**
	 * @description 用户授权
	 * @author mryhl
	 * @date 2020/10/11 16:15
	 * @return simpleAuthorizationInfo 简单授权信息
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
		System.out.println("=====================授权=====================");
		// 查询当前用户的权限信息
		User user = (User) principalCollection.getPrimaryPrincipal();
		List moduleList = userService.findModuleByUser(user);
		// 将信息传递到Shiro
		SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
		for (Module module : moduleList) {
			simpleAuthorizationInfo.addStringPermission(module.getName());
		}
		System.out.println(simpleAuthorizationInfo);
		return simpleAuthorizationInfo;
	}

3. 授权代码实现(注解版)

3.1 去掉xml中的注解配置

spring/applicationContext-shiro.xml



  
  
  
  
  
  
  
    
      /login.jsp = anon
      /css/** = anon
      /img/** = anon
      /plugins/** = anon
      /make/** = anon
      /login.do = anon
      
      
      /** = authc
    
  

3.2 使用注解声明资源访问权限

CompanyController.java

/**
	 * 查看列表
	 * name字段主要用于打印日志
	 */
	//@RequiresPermissions("企业管理")  代表只有用户有企业管理的权限,才能访问当前方法
	//相当于XML中的   /company/list.do = perms["企业管理"]
	@RequiresPermissions("企业管理")
	@RequestMapping(value = "/list",name = "企业列表查询")
	public String list(
			@RequestParam(defaultValue = "1", name = "page") Integer pageNum,
			@RequestParam(defaultValue = "1") Integer pageSize
	){
		PageInfo pageInfo = companyService.findByPage(pageNum, pageSize);
		request.setAttribute("page", pageInfo);
		return "company/company-list";
	}

3.3 全局异常处理修改

基于注解的授权控制, 当授权失败的时候, 不会跳转授权失败页面, 而是抛出一个异常, 需要我们在全局异常处理器中自己处理

package com.itheima.web.handlers;

import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.servlet.http.HttpServletRequest;

@ControllerAdvice
public class CommonExceptionHandler {

    //处理所有的Exception及其子类
    @ExceptionHandler(Exception.class)
    public String exceptionHandler(Exception e, HttpServletRequest request) {

        //1. 打印异常,给程序员
        e.printStackTrace();
        request.setAttribute("errorMsg", e.getMessage());

        //2. 返回页面, 给用户
        return "error";
    }


    //处理未授权的异常
    @ExceptionHandler(UnauthorizedException.class)
    public String UnauthorizedException(Exception e, HttpServletRequest request) {
        //2. 返回未授权页面, 给用户
        return "redirect:/unauthorized.jsp";
    }
}

4. 页面元素权限控制

4.1 说明

当用户没有相关资源的具体操作权限的时候,我们应该是不让其看到响应按钮的

这就要使用到了shiro标签, 这里主要介绍一个, 用法如下:



    

 

Shiro框架基础内容_第7张图片

4.2 代码

以部门管理为例子, 演示效果:

1 引入shiro标签库

<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>

2 使用shiro标签控制权限

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ include file="../../base.jsp" %>



    
        
        
        
        数据 - AdminLTE2定制版
        
        
        
        
    
    
    
        

系统管理 部门管理

部门列表

序号 编号 上级 名称 操作
${st.count } ${dept.id } ${dept.parent.deptName } ${dept.deptName }

5. 授权数据缓存

​ 我们现在访问被拦截的页面时,每次shiro的安全过滤器都需要从realm中获取认证方法,也意味这每次查询数据库,浪费服务性能,造成访问压力,这时候我们必须要进行优化:经常访问,但又不经常修改的这部分数据可以使用缓存

spring/applicationContext-shiro.xml






  
  

第五章 自定义Shiro过滤器(高级)

1. 需求明确

shiro 支持如下的权限写法, 代表同时拥有多个权限, 才能访问指定资源

/system/dept/edit.do = perms["新增部门","删除部门"]

但是我们现在有这样一个需求: 只需要满足其中一个个权限, 就要能对资源访问, 这时怎么办呢?

2. 自定义过滤器

操作步骤:

  1. 自定义过滤器, 继承AuthorizationFilter, 并实现里面的方法
  2. 配置文件中添加配置

自定义过滤器

package com.itheima.web.filters;

import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

public class MyPermissionsAuthorizationFilter extends AuthorizationFilter {

    public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {

        Subject subject = getSubject(request, response); //subject里面就有当前用户的权限    ["查看部门","新增部门","修改部门"]
        String[] perms = (String[]) mappedValue; //访问资源需要的权限 ["新增部门","删除部门"]

        //如果没有配置,放行
        if (perms == null || perms.length == 0) {
            return true;
        }

        for (String perm : perms) {
            if (subject.isPermitted(perm)) {
                return true;
            }
        }

        return false;
    }
}

修改配置文件


  
    
      /login.jsp = anon
      /css/** = anon
      /img/** = anon
      /plugins/** = anon
      /make/** = anon
      /login.do = anon
      
      
      /company/list.do = myPerms["新增部门","删除部门"]
      /** = authc
    
  
  
  
    
      
      
    
  



 

Shiro框架基础内容_第8张图片

你可能感兴趣的:(ssm框架,过滤器,java,shiro,spring,web)