引 言

 相关内容 : https://blog.csdn.net/superyayaya/article/details/94408805

 在web 中, 不同角色的用户, 具有不同的访问权限, 有的页面可以访问, 有的页面不可以访问, 那么如果不使用任何框架

你如何来解决这个问题? 你可能会建立一个表, 存储着不同用户对应不同的角色, 权限, 然后再每次访问页面的时候, 去查看当

前用户的权限,来判定当前页面他是否可以被访问; 如果在一个页面中有的内容, admin 可以看到, user 看不到, 那么你又会

怎么去做? 你可能会在js 写 一些逻辑判断语句,然显示一些admin 角色下的内容。

一、 什么是shiro

  Apache Shiro是一个强大且易用的Java安全框架,执行身份验证授权密码和会话管理。使用Shiro的易于理解的API,您可以

快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

二、ssm 框架下 如何使用shiro

  2.1、引入依赖

    <!-- 引入shiro依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.1</version>
</dependency> <dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.4.1</version>
</dependency> <dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency> <dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.4.1</version>
</dependency> <dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-quartz</artifactId>
<version>1.4.1</version>
</dependency>

  

  2.2  编写配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 一般是将什么东西如spring 进行整合的时候, 一般就是以bean 的方式 注册到 ioc 容器,已供后面使用 --> <!-- 首先下面是将这个bean注入到 Ioc 容器中, 然后供后面的过滤器使用(这个类实际上已经写好了) -->
<!-- 但是这个类里面的属性需要自己去设置,所以 才有下面的property 这些东西-->
<!-- 想一想之前你是怎么将一个一个类注册到ioc容器中的, 里面依赖的属性可以通过property的方式将他注入-->
<!-- 如果不使用下面的这个property的方法, 那么更方便的是使用autowired 的方式注入-->
<!-- 所以property 相当于 手动注入, autowired 相当于自动注入--> <!-- 下面这个bean 的设置是因为 web.xml 中的DelegatingFilterProxy会自动到Spring Ioc 容器中,寻找name为shiroFilter的bean,
并且将所有Filter的操作都委托给他管理(自己内部源码的机制)--> <!-- Shiro的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的安全管理器,所有关于安全的操作都会经过SecurityManager -->
<property name="securityManager" ref="securityManager"/>
<!-- 系统认证提交地址,如果用户退出即session丢失就会访问这个页面 -->
<property name="loginUrl" value="/login.jsp"/>
<!-- 权限验证失败跳转的页面,需要配合Spring的ExceptionHandler异常处理机制使用 -->
<!-- 当用户访问越过权利的url或者 jsp的时候, 用户处于未授权的状态,然后就会跳转到下面的url-->
<!-- 即用户访问了自己没有权利, 访问的页面的时候, 就会跳转到下面的页面里面去-->
<property name="unauthorizedUrl" value="/unauthorized.jsp"/>
<property name = "filterChainDefinitionMap" ref="filterChainDefinitionMap"/>
<!--<!– <property name="filters">–>-->
<!--<!– <util:map>–>-->
<!--<!– <entry key="authc" value-ref="formAuthenticationFilter"/>–>-->
<!--<!– </util:map>–>-->
<!--<!– </property>–>-->
<!-- <!– 自定义的过滤器链,从上向下执行,一般将`/**`放到最下面 –>-->
<!-- <!– anon : 表示可以匿名访问-->
<!-- authc:表示必须认证后(登录后)才可以访问的页面-->
<!-- logout: 登出的过滤器,用于将登录用户的信息进行销毁\-->
<!-- roles: 权限过滤器,用于对不同 url 和 jsp 页面进行访问权利设置-->
<!-- roles[user] 里面的“user” 就是一个字符串-->
<!-- –>-->
<!-- <property name="filterChainDefinitions">-->
<!-- <!– 下面这个value 这种方式很好吗?-->
<!-- 在很多问题的,权限的时候,可以进行方式的替换-->
<!-- 因为设置就是filterChainDefinitions, 所以可以通过类的方式再进行数据的匹配–>-->
<!-- <value>-->
<!-- /login.jsp = anon-->
<!-- /login = anon-->
<!-- /logout = logout-->
<!-- /user.jsp = roles[user]-->
<!-- /admin.jsp = roles[admin]-->
<!-- /** = authc-->
<!-- </value>-->
<!-- </property>-->
</bean> <!--配置一个bean, 该bean实际上一个Map, 通过实例工厂方法的方式实现的 -->
<bean id = "filterChainDefinitionMap"
factory-bean="filterChainDefinitionMapBuilder" factory-method="buildFilterChainDefinitionMap">
</bean>
<bean id = "filterChainDefinitionMapBuilder"
class = "com.shiro.factory.FilterChainDefinitionMapBuilder">
</bean> <!-- 安全管理器 -->
<!-- 下面这些属性的设置, 可能直接在DefaultWebSecurityManager类, 里面找不到-->
<!-- 是因为这个类一层一层实现其他类的结果, 所以可能设置是其他的类-->
<!-- 下面的属性 realms 原来是配置在authenticator 的realms 属性的 -->
<!-- 现在是将realms 配置在 securityManager的 realms -->
<!-- 现在内部还是会将realms 设置给 auhthenticator 的 realms 属性里面-->
<!-- 现在从认证上看, 没有什么变化, 但是现在给securityManager 配置 realms 属性 是为后面的授权做准备的 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name = "authenticator" ref = "authenticator"></property>
<property name = "realms">
<list>
<ref bean = "ShiroRealm" />
<ref bean = "SecondRealm" />
</list>
</property>
</bean> <!--因为下面这个类 ModularRealmAuthenticator,这个类里面专门用于对多个realm 操作的验证的方法-->
<!-- 设置属性list, 用于设置 ModularRealmAuthenticator 里面的 collection realms 属性-->
<!-- 默认使用的是AleastOneSuccessfulStrategy
public ModularRealmAuthenticator() {
this.authenticationStrategy = new AtLeastOneSuccessfulStrategy();
}
因为在他的构造方法里面设置了 认证策略
-->
<bean id = "authenticator" class = "org.apache.shiro.authc.pam.ModularRealmAuthenticator">
<property name = "authenticationStrategy" >
<bean class = "org.apache.shiro.authc.pam.AllSuccessfulStrategy"></bean>
</property>
</bean> <!-- 对下面的realm 设置的时候也可以配置相应的缓存
因为在很多权限页面访问时候,需要验证权限的时候,需要每次去调用验证权限的方法
但是在一次会话中,用户的权限是一定的, 所以可以给shiro 设置权限来解决,提高
代码的效率, 但是现在我还不怎么会配置, 可不可以之前像使用redis 缓存一样
直接在方法上面添加缓存的标签吗?
或者又是什么方法?
--> <!-- 第一个Realm实现 -->
<!-- 配置realm 配置 中 对获取前台密码加密方式的的类 -->
<bean id = "SecondRealm" class = "com.shiro.ShiroRealm">
<property name = "credentialsMatcher">
<!-- 配置验证原则 -->
<bean class = "org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="MD5"></property>
<property name="hashIterations" value = "1024"></property>
</bean>
</property>
</bean>

<!-- 第二个Realm实现 -->
<!-- 配置realm 配置 中对 密码加密的类 -->
<bean id = "ShiroRealm" class = "com.shiro.SecondRealm">
<property name = "credentialsMatcher">
<bean class = "org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="SHA1"></property>
<property name="hashIterations" value = "1024"></property>
</bean>
</property>
</bean>
</beans>

  

  2.3 编写realm

package com.shiro;

import org.apache.shiro.authc.*;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthenticatingRealm;
/**
* 现在是使用多个不同的realm 验证
* 因为安全数据可能存放在不同的数据库中,不同的数据会有不同的加密原则
* 所以配置不同加密原则的realm 用于匹配不同的类型的安全数据
*/
public class SecondRealm extends AuthenticatingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// AuthenticationInfo 这是一个接口, 然后实现他的实现类 UsernamePasswordToken upToken = (UsernamePasswordToken) token;
System.out.println("--> SencondRealm"); // 查看token里面username, password 这些参数
String username = upToken.getUsername();
char[] password = upToken.getPassword(); //最开始传入的是字符串,在usernamePassword 里面就进行了char[] 的转化工作 // 如果是不知道的用户, 抛出 异常unknownAccountException 异常
if("unknow".equals(username)){
throw new UnknownAccountException("用户名字不存在");
}
// 如果用户被锁定, 则抛出异常 LockedAccoutException
if("monster".equals(username)){
throw new LockedAccountException("用户名被锁定");
}
// 然后你需要从数据库中查询出来 用户名,密码用于realm 的验证
// 验证经过数据库查询出来的密码 和 token里面前端携带的用户密码之间是否相同
// 源码中 可能是将 info 和token 之间进行对比分析的, 即shiro 会完成密码的比对
Object principal = username; // 这个好像无所谓
Object credentials = "a94d5cd0079cfc8db030e1107de1addd1903a01b";
String realmName = getName(); // info 专门用来存储 经过查询数据库后的用户信息
// shiro 通过比较token,info 来进行数据的确定
AuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, realmName);
return info;
} public static void main(String[] args){
String hashAlgorithmName = "SHA1";
Object credentials = "123456";
Object salt = null;
int hashIterations = 1024;
Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
System.out.println(result);
}
}

  

三、 使用shiro 实现认证登录

实现流程: 用户在登录页面点击登录, 访问登录对应的url, 将用户名和密码封装成 Token 对象 , 执行 Login 方法,然后

authenticator 认证器下, 调用realm  的  doGetAuthenticationInfo方法,  将用户信息对象 token 传递给

realm,根据token对象中用户名信息查询用户在数据中的用户的密码信息, 并将密码信息封装成 AuthenticationInfo 对象进

行返回,在authenticator内部通过对Token 对象 以及  AuthenticationInfo 对象 进行对比, 来进行用户验证。

问题:查一个基本的用户登录为什么要这么的复杂, 直接用户输入的密码和用户的数据库中密码进行比对不就ok了, 感觉

    需要像上面这样复杂?

原因:第一: 一般在数据库中存储的密码不是明文,都经过加密操作,然后用户登录时候输入的密码, 你需要进行加密操作后, 再

进行对比,shiro 中就从表面上看较为简单的实现, 只需要在配置realm的文件中 配置 加密算法, 加密次数, shiro 内部就实现

了对你用户输入的密码进行加密操作

             <property name="hashAlgorithmName" value="MD5"></property>
<property name="hashIterations" value = "1024"></property> 第二:如果在用户登录时候, 如何查看用用户的权限?shiro 提供了一个 authorizer 授权器,用于查询用户的权限信息,当用户
登录成功authorizer 会调用 realm的doGetAuthorizationInfo 方法,根据 PrincipalCollection 信息中的用户名信息,进行数据
库中查询用户的权限信息,并将权限信息封装成SimpleAuthorizationInfo对象返回
四、shiro jstl 标签的使用
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> // 引入shiro标签库
<html>
<head>
<title>Title</title>
</head>
<h1> List </h1>
<a href = "user.jsp"> user </a>
<br><br> <a href = "admin.jsp"> admin </a>
<br><br> <a href = "test"> test admin.jsp </a>
<br><br> <a href = "cache">test cache </a>
<br><br> <%--shiro jstl 标签能够根据用户的角色和权限, 显示不同的内容--%> <%--没有认证的显示的内容--%>
<shiro:guest>
everyBody can get
<br><br>
</shiro:guest> <%--只有角色admin,才显示--%>
<shiro:hasRole name="admin">
Only admin can get
<br><br>
</shiro:hasRole> <shiro:hasRole name = "user">
only user can get
<br><br>
</shiro:hasRole> <%--只有认证过的用户,才显示--%>
<shiro:authenticated>
only authenticated user can get
<br><br>
</shiro:authenticated> <a href="logout"> logout </a>
<%--如果没有登出, shiro的缓存里面存储着用户的登录信息, 然后用户登录都会成功--%>
</body>
</html>
 
五、 用户权限的注解开发

  使用@RequiresRoles(“xxx”)标注的方法, controller 只有 对应的 权限的用户才能够访问

    @RequiresRoles("admin")
@RequestMapping("/test")
public String test(HttpSession session){
session.setAttribute("key", "hello");
serviceTest.testSession(session);
return "redirect: admin.jsp";
}

  

六、 shiro 的 缓存使用

  因为在页面中使用 shiro 的 jstl 标签, 会去不断的去查询当前用户的权限信息, 所以这里进行优化的话

就是使用缓存, 可以使用ehcache , redis 等 缓存来实现




还是写的太垃圾了, 忘见谅

  

shiro 基础使用的更多相关文章

  1. Shiro基础

    Factory<T>接口(org.apache.shiro.util.Factory) 接口的getInstance()方法是泛型方法,可以用来创建SecurityManager接口对象 ...

  2. Shiro 基础教程

    原文地址:Shiro 基础教程 博客地址:http://www.extlight.com 一.前言 Apache Shiro 是 Java 的一个安全框架.功能强大,使用简单的Java安全框架,它为开 ...

  3. Apache Shiro:【1】Shiro基础及Web集成

    Apache Shiro:[1]Shiro基础及Web集成 Apache Shiro是什么 Apache Shiro是一个强大且易于使用的Java安全框架,提供了认证.授权.加密.会话管理,与spri ...

  4. Shiro框架 - 【shiro基础知识】

     转载:https://segmentfault.com/a/1190000013875092#articleHeader27  读完需要 63 分钟   前言 本文主要讲解的知识点有以下: 权限管理 ...

  5. Apache Shiro 快速入门教程,shiro 基础教程 (这篇文章非常好)

    第一部分 什么是Apache Shiro     1.什么是 apache shiro :   Apache Shiro是一个功能强大且易于使用的Java安全框架,提供了认证,授权,加密,和会话管理 ...

  6. Apache Shiro 快速入门教程,shiro 基础教程

    第一部分 什么是Apache Shiro     1.什么是 apache shiro :   Apache Shiro是一个功能强大且易于使用的Java安全框架,提供了认证,授权,加密,和会话管理 ...

  7. 【原】Spring整合Shiro基础搭建[3]

    1.前言 上个Shiro Demo基础搭建是基于官方的快速入门版本,没有集成其他框架,只是简单的通过Main方法来执行Shiro工作流程,并测试一下比较核心的函数:但在企业开发中一般都会集成Sprin ...

  8. Shiro基础知识08----拦截器介绍(转)

    1 拦截器介绍 Shiro使用了与Servlet一样的Filter接口进行扩展:所以如果对Filter不熟悉可以参考<Servlet3.1规范>http://www.iteye.com/b ...

  9. shiro基础学习(四)—shiro与项目整合

    一.认证 1.配置web.xml   2.配置applicationContext.xml      在applicationContext.xml中配置一个bean,ID和上面的过滤器的名称一致. ...

  10. shiro基础学习(三)—shiro授权

    一.入门程序 1.授权流程        2.授权的三种方式 (1)编程式: 通过写if/else 授权代码块完成. Subject subject = SecurityUtils.getSubjec ...

随机推荐

  1. Python - Tuple 怎么用,为什么有 tuple 这种设计?

    背景 看到有同学很执着的用 tuple,想起自己刚学 python 时,也是很喜欢 tuple,为啥?因为以前从来没见过这种样子的数据 (1,2), 感觉很特别,用起来也挺好用 i,j=(1,2), ...

  2. git之github解决冲突

    1.先创建一个txt文件,并进行编辑 2.然后推送到github,过程看之前的教程. 3.在另一个文件夹拉取(用小乌龟拉取),分别在克隆文件夹和原本文件夹操作test.txt. 4.把本体推送给服务器 ...

  3. 通过9个Linux-0.11实验学习操作系统

    简介 2019年秋,我自学了一下哈工大的操作系统课程,感觉其设计的教程和实验作为操作系统入门是个不错的选择(虽然是基于较老的Linux-0.11写的).实验大致覆盖了操作系统中的核心概念,例如启动.中 ...

  4. vue学习笔记(四)事件处理器

    前言 在上一章vue学习笔记(三)class和style绑定的内容中,我们学习了如何在vue中绑定class和style,介绍了常用的绑定方法,class的数组绑定和对象绑定以及style的数组绑定和 ...

  5. 20191114-2 Beta阶段事后诸葛亮会议

    此作业要求参见:https://edu.cnblogs.com/campus/nenu/2019fall/homework/10005 组长组“多彩夕阳”项目beta阶段诸葛亮会议 设想和目标 1.我 ...

  6. 【一起学源码-微服务】Ribbon 源码一:Ribbon概念理解及Demo调试

    前言 前情回顾 前面文章已经梳理清楚了Eureka相关的概念及源码,接下来开始研究下Ribbon的实现原理. 我们都知道Ribbon在spring cloud中担当负载均衡的角色, 当两个Eureka ...

  7. 你的IDEA过期了?跃哥四大招帮你稳住

    作者:Dimple Solgan:当你的才华还无法撑起你的野心时候,那应该静下心来好好学习 前天晚上在群里风风火火组建了两个学习小组,一个是面向Java初学,一个是面向Python初学,把我搞的兴奋不 ...

  8. 「CH2201」小猫爬山 解题报告

    CH2201 小猫爬山 背景 Freda和rainbow饲养了N只小猫,这天,小猫们要去爬山.经历了千辛万苦,小猫们终于爬上了山顶,但是疲倦的它们再也不想徒步走下山了(呜咕>_<). 描述 ...

  9. 使用Theia——创建插件

    上一篇:使用Theia——创建扩展包 创建Theia插件 下面我们来看看如何创建Theia插件.作为示例,我们将注册一个Hello World命令,该命令显示一个“Hello World”通知.本文将 ...

  10. 关于ESP8266 NodeCMU固件无法刷入新代码的解决方法

    在玩ESP8266时,有时候会无意中写了导致死循环的代码,或都某些函数传递了不合适的参数导致系统崩溃,这可能会导致ES8266不停地重启,这时我们发现无法刷入新的代码,也无法删除8266中的原代码.我 ...