shiro (java安全框架)——我的笔记

shiro (java安全框架)


Shiro简介

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
• Shiro 可以完成:认证、授权、加密、会话管理、与Web 集成、缓存等
下载:http://shiro.apache.org/

  • Shiro的知识准备

一、基本功能点如下图所示:

在这里插入图片描述

二、功能简介

Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能进行什么操作,如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境,也可以是 Web 环境的;
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
Web Support:Web 支持,可以非常容易的集成到Web 环境;
Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
Concurrency:Shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
Testing:提供测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了

三、Shiro 架构(Shiro外部来看)

在这里插入图片描述

Subject:应用代码直接交互的对象是 Subject,也就是说 Shiro 的对外API 核心就是 Subject。Subject 代表了当前“用户”, 这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等;与 Subject 的所有交互都会委托给 SecurityManager;Subject 其实是一个门面,SecurityManager 才是实际的执行者;
SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager 交互;且其管理着所有 Subject;可以看出它是 Shiro的核心,它负责与 Shiro 的其他组件进行交互,它相当于 SpringMVC 中DispatcherServlet 的角色
Realm:Shiro 从 Realm 获取安全数据(如用户、角色、权限),就是说SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色/权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource

四、Shiro 架构(Shiro内部来看)

在这里插入图片描述

Subject:任何可以与应用交互的“用户”;
SecurityManager :相当于SpringMVC 中的 DispatcherServlet;是 Shiro 的心脏;
所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证、授权、会话及缓存的管理。
Authenticator:负责 Subject 认证,是一个扩展点,可以自定义实现;可以使用认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
Authorizer:授权器、即访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能
Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC 实现,也可以是内存实现等等;由用户提供;所以一般在应用中都需要实现自己的 Realm;
SessionManager:管理 Session 生命周期的组件;而 Shiro 并不仅仅可以用在 Web环境,也可以用在如普通的 JavaSE 环境
CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少改变,放到缓存中后可以提高访问的性能
Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密/解密。

五、集成 Spring

> 加入 Spring 和 Shiro 的 jar 包
> 配置 Spring 及 SpringMVC
> 参照:1.3.2\shiro-root-1.3.2-source-release\shiro-root-1.3.2\samples\spring 配置
web.xml 文件和 Spring 的配置文件

六、与WEB集成

•   Shiro 提供了与 Web 集成的支持,其通过一个ShiroFilter 入口来拦截需要安全控制的URL,然后
	进行相应的控制
•   ShiroFilter 类似于如 Strut2/SpringMVC 这种web 框架的前端控制器,是安全控制的入口点,其
	负责读取配置(如ini 配置文件),然后判断URL是否需要登录/权限等工作。

七、ShiroFilter(web.xml文件)

DelegatingFilterProxy 作用是自动到 Spring 容器查找名字为
shiroFilter(filter-name)的 bean 并把所有 Filter的操作委托给它。

	<!-- The filter-name matches name of a 'shiroFilter' bean inside applicationContext.xml -->
<filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
        <param-name>targetFilterLifecycle</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>

<!-- Make sure any request you want accessible to Shiro is filtered. /* catches all -->
<!-- requests.  Usually this filter mapping is defined first (before all others) to -->
<!-- ensure that Shiro works in subsequent filters in the filter chain:             -->
<filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
部分细节

在这里插入图片描述

在这里插入图片描述

URL 匹配模式

在这里插入图片描述

URL 匹配顺序
URL 权限采取第一次匹配优先的方式,即从头开始
使用第一个匹配的 url 模式对应的拦截器链。
• 如:
/bb/**=filter1
/bb/aa=filter2
/**=filter3
如果请求的url是“/bb/aa”,因为按照声明顺序进行匹
配,那么将使用 filter1 进行拦截。

八、认证

身份验证
身份验证:一般需要提供如身份 ID 等一些标识信息来表明登录者的身份,如提供 email,用户名/密码来证明。
• 在 shiro 中,用户需要提供 ==principals (身份)和 credentials(证明)==给 shiro,从而应用能验证用户身份:
principals:身份,即主体的标识属性,可以是任何属性,如用户名、邮箱等,唯一即可。一个主体可以有多个 principals,但只有一个Primary principals,一般是用户名/邮箱/手机号。
credentials证明/凭证,即只有主体知道的安全值,如密码/数字证书等。
• 最常见的 principals 和 credentials 组合就是用户名/密码
身份验证基本流程
• 1、收集用户身份/凭证,即如用户名/密码
• 2、调用 Subject.login 进行登录,如果失败将得到相应的 AuthenticationException 异常,根据异常提示用户错误信息;否则登录成功
• 3、创建自定义的 Realm 类,继承org.apache.shiro.realm.AuthorizingRealm 类,实现doGetAuthenticationInfo() 方法
示例
  /**
         	* 如果是没有认证的用户
         */
        if (!currentUser.isAuthenticated()) {//判断当前用户是否认证过
            UsernamePasswordToken token = new UsernamePasswordToken("root", "secret");
            token.setRememberMe(true);
            try {
                currentUser.login(token);
            } catch (UnknownAccountException uae) {
            	//账号不存在
                log.info("------->账号不存在 " + token.getPrincipal());
            } catch (IncorrectCredentialsException ice) {
            	//密码不匹配
                log.info("------->密码错误 " + token.getPrincipal() + " was incorrect!");
                //账号锁定
            } catch (LockedAccountException lae) {
                log.info("The account for username " + token.getPrincipal() + " is locked.  " +
                        "Please contact your administrator to unlock it.");
            }
            // ... catch more exceptions here (maybe custom ones specific to your application?
            catch (AuthenticationException ae) {
                //unexpected condition?  error?
            }
        }
AuthenticationException
• 如果身份验证失败请捕获 AuthenticationException 或其子类
• 最好使用如“用户名/密码错误”而不是“用户名错误”/“密码错误”,防止一些恶意用户非法扫描帐号库;

在这里插入图片描述

认证流程

在这里插入图片描述

•    1、首先调用 Subject.login(token) 进行登录,其会自动委托给SecurityManager
•    2、SecurityManager 负责真正的身份验证逻辑;它会委托给Authenticator 进行身份验证;
•    3、Authenticator 才是真正的身份验证者,Shiro API 中核心的身份认证入口点,此处可以自定义插入
自己的实现;
•    4、Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm身份验证,默认 
ModularRealmAuthenticator 会调用AuthenticationStrategy 进行多 Realm 身份验证;
•    5、Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返回/抛出异常表示
身份验证失败了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问。

九、Realm

• Realm:Shiro 从 Realm 获取安全数据(如用户、角色、权限),即 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作
• Realm常见实现类如下:

在这里插入图片描述

一般继承 AuthorizingRealm(授权)即可;其继承了AuthenticatingRealm(即身份验证),而且也间接继承了CachingRealm(带有缓存实现)。
• Realm 的继承关系:

在这里插入图片描述

十、MD5加密

	public static void main(String[] args) {
		/**   加密
		 * new SimpleHash(algorithmName, source, salt, hashIterations)
		 *algorithmName 加密算法的名称
		 *source 需要加密的内容
		 *salt 盐值,初学一般为用户名,只要用户名唯一,产生出来的密码就不一样
		 *hashIterations 加密的次数
		 */
//		System.out.println(new SimpleHash("Md5", "123456", "admin", 1024));
		System.out.println(new SimpleHash("MD5", "123456", "admin", 1024).toString());
	}

十一、授权

授权,也叫访问控制,即在应用中控制谁访问哪些资源(如访问页面/编辑数据/页面操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)。
主体(Subject):访问应用的用户,在 Shiro 中使用 Subject 代表该用户。用户只有授权后才允许访问相应的资源。
资源(Resource)在应用中用户可以访问的 URL,比如访问 JSP 页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。
权限(Permission):安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如:访问用户列表页面查看/新增/修改/删除用户数据(即很多时候都是CRUD(增查改删)式权限控制)等。权限代表了用户有没有操作某个资源的权利,即反映在某个资源上的操作允不允许。
Shiro 支持粗粒度权限(如用户模块的所有权限)和细粒度权限(操作某个用户的权限,即实例级别的)
角色(Role)权限的集合,一般情况下会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师等都是角色,不同的角色拥有一组不同的权限。
授权方式
• Shiro 支持三种方式的授权:
–   编程式:通过写if/else 授权代码块完成
–   注解式:通过在执行的Java方法上放置相应的注解完成,没有权限将抛出相应的异常
–   JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

在这里插入图片描述

默认拦截器
• Shiro 内置了很多默认的拦截器,比如身份验证、授权等相关的。默认拦截器可以参考org.apache.shiro.web.filter.mgt.DefaultFilter中的枚举拦截器:

在这里插入图片描述

授权相关

在这里插入图片描述

其他

在这里插入图片描述

添加权限的实现
1、继承AuthorizingRealm
2、doGetAuthorizationInfo(PrincipalCollection principals) principals获取当前用户信息
3、实现
Set<String> roles=new HashSet<String>();
roles.add("user");
if(principals.getPrimaryPrincipal().equals("admin")){
roles.add("admin");
}
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo(roles);

添加细节权限(细节到恶心)
// 获取身份信息
 String username = (String) principals.getPrimaryPrincipal(); 
// 根据身份信息从数据库中查询权限数据 /
/....这里使用静态数据模拟 
List<String> permissions = new ArrayList<String>(); 
permissions.add("user:create"); 
permissions.add("user.delete"); 
//将权限信息封闭为
AuthorizationInfo SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); 
for(String permission:permissions){
 simpleAuthorizationInfo.addStringPermission(permission);
 }

十二、Shiro标签(欺骗一般人士的标签)

• Shiro 提供了 JSTL 标签用于在 JSP 页面进行权限控制,如根据登录用户显示相应的页面按钮。
• guest 标签:用户没有身份验证时显示相应信息,即游客访问信息:
• user 标签:用户已经经过认证/记住我登录后显示相应的信息。
• authenticated 标签:用户已经身份验证通过,即Subject.login登录成功
• notAuthenticated 标签:用户未进行身份验证,即没有调用Subject.login进行登录,包括记住我自动登录的也属于未进行身份验证。
• pincipal 标签:显示用户身份信息,默认调用Subject.getPrincipal() 获取,即 Primary Principal。
• hasRole 标签:如果当前 Subject 有角色将显示 body 体内容
• hasAnyRoles 标签:如果当前Subject有任意一个角色(或的关系)将显示body体内容。
• lacksRole:如果当前 Subject 没有该角色将显示 body 体内容
<shiro:user>
<shiro:principal/>
</shiro:user>

<a href="loginout">注销</a>

<!-- 多种角色显示的模块 -->
<shiro:hasAnyRoles name="admin,test">
测试模块
</shiro:hasAnyRoles>

<!-- shiro:lacksRole name="admin"   非哪种角色显示的模块 -->
<shiro:lacksRole name="admin">
非管理员模块
</shiro:lacksRole>

<!-- 哪种角色显示的模块 -->
<shiro:hasRole name="admin">
<a href="javascript:void(0)" onclick="admin()">管理员</a>
</shiro:hasRole>
	网站首页
	
	<shiro:guest>
		亲,请<a hrdf="login">登录</a> <a hrdf="register">注册</a>
	</shiro:guest>
	<shiro:user>
		欢迎你!<shiro:principal/>
	</shiro:user>

十三、权限注解

@RequiresAuthentication:表示当前Subject已经通过login进行了身份验证;即 Subject. isAuthenticated() 返回 true
@RequiresUser:表示当前 Subject 已经身份验证或者通过记住我登录的。
@RequiresGuest:表示当前Subject没有身份验证或通过记住我登录过,即是游客身份。
@RequiresRoles(value={“admin”, “user”}, logical= Logical.AND):表示当前 Subject 需要角色 admin 和user
@RequiresPermissions (value={“user:a”, “user:b”}, logical= Logical.OR):表示当前 Subject 需要权限 user:a 或 user:b。

  • 简单运用(会用到大部分常用知识)

loginuser表

在这里插入图片描述

role表

在这里插入图片描述

user_role表

在这里插入图片描述

pom.xml(引入相关的jar包,包括SSM三大框架)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.hqyj</groupId>
  <artifactId>1809_shiro-test</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  
     <dependencies>
  	<!-- spring基本依赖 -->
  		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.3.20.RELEASE</version>
			<scope>runtime</scope>
		</dependency>
		
	<!-- springmvc依赖 -->	
	<dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-webmvc</artifactId>
   		 <version>4.3.20.RELEASE</version>
	</dependency>
	
	<!-- JSON核心包 -->
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.9.7</version>
</dependency>

<!-- JSON数据绑定包 -->
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.7</version>
</dependency>
  
  <!-- 引入mybatis的依赖 -->
  		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.4.6</version>
		</dependency>
		<!-- 引入数据库支持 -->
		<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.40</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
		<!-- 依赖aspectj.weaver.jar -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.9.2</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
		<!-- 依赖aopalliance.jar -->
		<dependency>
			<groupId>aopalliance</groupId>
			<artifactId>aopalliance</artifactId>
			<version>1.0</version>
		</dependency>
		
				<!-- c3p0连接池工具 -->
		<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
		<dependency>
			<groupId>com.mchange</groupId>
			<artifactId>c3p0</artifactId>
			<version>0.9.5.2</version>
		</dependency>
		
		<!-- 引入mybatis对spring框架整合的jar包 -->
		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
	<dependency>
   		 <groupId>org.mybatis</groupId>
   		 <artifactId>mybatis-spring</artifactId>
    	<version>1.3.2</version>
	</dependency>
		  
		  		<!-- spring-jdbc事务依赖 -->
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>4.3.20.RELEASE</version>
		</dependency>
		
		<!-- 对shiro的支持 -->
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-all -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-all</artifactId>
    <version>1.3.2</version>
</dependency>

		
  
  </dependencies>
  
  
</project>
web.xml文件(配置springmvc核心,监听器,shiro安全框架)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">

<!--监听器-->
<!-- needed for ContextLoaderListener -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:applicationcontext-dao.xml,classpath:applicationcontext-shiro.xml</param-value>
	</context-param>

	<!-- Bootstraps the root web application context before servlet initialization -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>


<!--springmvc核心-->
<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
	<servlet>
		<servlet-name>springDispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:applicationcontext-springmvc.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Map all requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>springDispatcherServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
	<!--定义过滤器和映射-->
	<!-- The filter-name matches name of a 'shiroFilter' bean inside applicationContext.xml -->
<filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
        <param-name>targetFilterLifecycle</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>

<!-- Make sure any request you want accessible to Shiro is filtered. /* catches all -->
<!-- requests.  Usually this filter mapping is defined first (before all others) to -->
<!-- ensure that Shiro works in subsequent filters in the filter chain:             -->
<filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>	
	
	

</web-app>
applicationcontext-springmvc(对springmvc的全局配置)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

<context:component-scan base-package="com.hqyj.action"></context:component-scan>

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="prefix" value="/WEB-INF/page/"></property>
	<property name="suffix" value=".jsp"></property>

</bean>

<mvc:annotation-driven></mvc:annotation-driven>
<mvc:default-servlet-handler/>


</beans>

applicationcontext-dao(对mybatis的全局配置)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

<context:component-scan base-package="com.hqyj.service"></context:component-scan>
<context:property-placeholder location="classpath:db.properties"/>

<!-- 配置数据库连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${driver}"></property>  
        <property name="jdbcUrl" value="${url}"></property>  
        <property name="user" value="${user}"></property>  
        <property name="password" value="${password}"></property>  
        <property name="initialPoolSize" value="10"></property>  
        <property name="maxIdleTime" value="30"></property>  
        <property name="maxPoolSize" value="100"></property>  
        <property name="minPoolSize" value="10"></property>  
        <property name="maxStatements" value="200"></property>  
</bean>	

<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactoryBean">
	<property name="dataSource" ref="dataSource"></property>
	<property name="configLocation" value="classpath:mybatis-config.xml"></property>
	<property name="mapperLocations" value="classpath*:mappers/**/*.xml"></property>

</bean>

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<property name="basePackage" value="com.hqyj.dao"></property>

</bean>

<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
	<property name="dataSource" ref="dataSource"></property>
</bean>

<tx:annotation-driven transaction-manager="transactionManager"/>

</beans>

applicationcontext-shiro(对shiro的全局配置)
<?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">



<!-- 配置shiroFilter -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager"/>
    <!-- override these for application-specific URLs if you like:
    <property name="loginUrl" value="/login.jsp"/>
    <property name="successUrl" value="/home.jsp"/>
    <property name="unauthorizedUrl" value="/unauthorized.jsp"/> -->
    <!-- The 'filters' property is not necessary since any declared javax.servlet.Filter bean  -->
    <!-- defined will be automatically acquired and available via its beanName in chain        -->
    <!-- definitions, but you can perform instance overrides or name aliases here if you like: -->
    <!-- <property name="filters">
        <util:map>
            <entry key="anAlias" value-ref="someFilter"/>
        </util:map>
    </property> -->
    
    <!-- 未认证之前默认的跳转地址 -->
    <property name="loginUrl" value="/login"/>
    
    <!-- 未授权的url地址 -->
    <!-- <property name="unauthorizedUrl" value="/url?url=unauthorized"/> -->
    <property name="unauthorizedUrl" value="/unauthorized"/>
    
    <!-- 拦截规则 -->
    <property name="filterChainDefinitions">
        <value>
        
        <!-- 优先匹配,只要匹配上一个,后面的不在遵循 -->
            # some example chain definitions:
            # anon: 代表匿名访问 
            # authc: 代表认证访问
            # logout: 代表注销
            # roles[admin]:需要哪些权限
            
            /login=anon
            /testlogin=anon
            /jquery/**=anon
            /register/**=anon
            /registeraction/**=anon
            /registersuccess/**=anon
            /loginout=logout
            /url=anon
            /jump/** = anon
            /admin/** = authc
<!--             /admin/** = authc, roles[admin]
            /docs/** = authc, perms[document:read] -->
            /** = authc
            # more URL-to-FilterChain definitions here
        </value>
    </property>
</bean>

<!-- Define any javax.servlet.Filter beans you want anywhere in this application context.   -->
<!-- They will automatically be acquired by the 'shiroFilter' bean above and made available -->
<!-- to the 'filterChainDefinitions' property.  Or you can manually/explicitly add them     -->
<!-- to the shiroFilter's 'filters' Map if desired. See its JavaDoc for more details.       -->
<!-- <bean id="someFilter" class=""/>
<bean id="anotherFilter" class="">  </bean> -->

<!-- securityManager:管理所有的会话 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <!-- Single realm app.  If you have multiple realms, use the 'realms' property instead. -->
    <property name="realm" ref="myRealm"/>
    <!-- By default the servlet container sessions will be used.  Uncomment this line
         to use shiro's native sessions (see the JavaDoc for more): -->
    <!-- <property name="sessionMode" value="native"/> -->
</bean>

<!-- lifecycleBeanPostProcessor: 生命周期的管理 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

<!-- Define the Shiro Realm implementation you want to use to connect to your back-end -->
<!-- security datasource: -->

<!-- 加载资源包 -->
<bean id="myRealm" class="com.hqyj.shiro.MyRealm">
   
</bean>

<!-- Enable Shiro Annotations for Spring-configured beans.  Only run after -->
<!-- the lifecycleBeanProcessor has run: -->

<!-- 对shiro注解的支持 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    <property name="securityManager" ref="securityManager"/>
</bean> 

</beans>

mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

<!--系统设置 -->
<settings>
	<!-- 设置二级缓存 -->
	<setting name="cacheEnabled" value="true"/>
	<!--默认设置懒加载 -->
	<setting name="lazyLoadingEnabled" value="true"/>
</settings>

<!-- <typeAliases>
取别名
	<typeAlias type="" alias=""/>
</typeAliases> -->

 
</configuration>
loginuser.xml(mybatis映射文件)
<?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">
  
  <!-- namespace:名称空间 :面向接口编程的时候,必须为接口所在位置 -->
<mapper namespace="com.hqyj.dao.LoginUserDao">
	<select id="findBynameandPwd" resultType="com.hqyj.bean.LoginUser">
		select * from loginuser where username = #{username} and password = #{password}
	</select>
	
	<select id="findByname" resultType="com.hqyj.bean.LoginUser">
		select * from loginuser where username = #{username}
	</select>
	
	<insert id="addUser">
		insert into loginuser values(#{id},#{username},#{password});
	</insert>
	
	<select id="findRole" resultMap="rolemap">
		SELECT rolename FROM user_role where username = #{username}
	</select>
  
  	<resultMap type="com.hqyj.bean.UserRole" id="rolemap">
  		<result property="rolename" column="rolename"/>
  	</resultMap>
  
  
</mapper>
db.propercties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/1809_shiro_test?characterEncoding=utf-8&useSSL=false
user=root
password=963521
LoginUser.java
package com.hqyj.bean;

public class LoginUser {
	private int id;
	private String username;
	private String password;
	private int r_id;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public int getR_id() {
		return r_id;
	}
	public void setR_id(int r_id) {
		this.r_id = r_id;
	}
	@Override
	public String toString() {
		return "LoginUser [id=" + id + ", username=" + username + ", password=" + password + ", r_id=" + r_id + "]";
	}
	public LoginUser() {
		super();
		// TODO Auto-generated constructor stub
	}
	
	
	
}

UserRole.java
package com.hqyj.bean;

public class UserRole {

	private int id;
	private String username;
	private String rolename;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getRolename() {
		return rolename;
	}
	public void setRolename(String rolename) {
		this.rolename = rolename;
	}
	public UserRole() {
		super();
		// TODO Auto-generated constructor stub
	}
	@Override
	public String toString() {
		return "UserRole [id=" + id + ", username=" + username + ", rolename=" + rolename + "]";
	}
	
	
	
}

LoginUserDao.java
package com.hqyj.dao;


import java.util.List;

import com.hqyj.bean.LoginUser;
import com.hqyj.bean.UserRole;

public interface LoginUserDao {
	
	public LoginUser findBynameandPwd(LoginUser user);
	public LoginUser findByname(LoginUser user);
	public void addUser(LoginUser user);
	
	public List<UserRole> findRole(LoginUser user);
}

LoginUserService.java
package com.hqyj.service;

import java.util.List;

import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.hqyj.bean.LoginUser;
import com.hqyj.bean.UserRole;
import com.hqyj.dao.LoginUserDao;

@Service
public class LoginUserService {
	@Autowired
	private LoginUserDao dao;
	
	public LoginUser findBynameandPwd(LoginUser user) {
		return dao.findBynameandPwd(user);
	}
	
	public List<UserRole> findRole(LoginUser user){
		return dao.findRole(user);
	}
	
	//权限管理,没有admin权限无法进入,会报500错误
	@RequiresRoles(value= {"admin"})
	public void test() {
		System.out.println("我被调用了");
	}
}

LoginAction.java
package com.hqyj.action;


import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.hqyj.bean.LoginUser;
import com.hqyj.dao.LoginUserDao;

@Controller
public class LoginAction {

	@Autowired
	private LoginUserDao dao;
	
	@RequestMapping("/login")
	public String login() {
		return "index";
	}
	
	
	
	@RequestMapping("/testlogin")
	@ResponseBody
	public String test(@RequestParam("username")String username,@RequestParam("password")String password) {
		Subject currentuser = SecurityUtils.getSubject();
		if(!currentuser.isAuthenticated()) {
			UsernamePasswordToken token = new UsernamePasswordToken(username,password);
			token.setRememberMe(true);
			try {
				currentuser.login(token);
			}catch (AuthenticationException e) {
				return "fail";
			}
		}	
		return "success";
	}
	
	
	
	



	// 注册页面
	@RequestMapping("/register")
	public String register() {

		return "register";
	}

	// 注册检测
	@RequestMapping("/registeraction")
	@ResponseBody
	public String registeraction(LoginUser user) {
		if (user.getUsername() == null || user.getUsername().equals("")) {
			return "null";
		} else {
			LoginUser u = dao.findByname(user);
			if (u != null) {
				return "fail";
			} else {
				return "ok";
			}
		}
	}
	
	// 注册
	@RequestMapping("/registersuccess")
	public String registersuccess(LoginUser user) {
		System.out.println(user);
		String username = user.getUsername();
		String password = user.getPassword();
		
		user.setPassword(new SimpleHash("MD5", password, username, 1024).toString());
		
		dao.addUser(user);
		return "login";
	}

	// 登录成功跳转页面
	@RequestMapping("/success")

	public String dologin() {
		return "success";
	}

}

JumpUtil.java(跳转工具类)
package com.hqyj.action;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class JumpUtil {

	@RequestMapping("/jump")
	public String jump(@RequestParam("url") String url) {
		return url;
	}
}

Unauthorized.java(没有权限返回ajax请求的类)
package com.hqyj.action;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class Unauthorized {
	
	@RequestMapping("unauthorized")
	@ResponseBody
	public String unauthorized() {
		return "sorry";
	}
}

GlobeException.java(全局异常类)
package com.hqyj.action;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class GlobeException {
	
	@ExceptionHandler(value= {RuntimeException.class})
	public String exception() {
		return "unauthorized";
	}
}

AdminAction.java(有权限跳转成功返回ajax请求的类)
package com.hqyj.action;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.hqyj.service.LoginUserService;

@Controller
@RequestMapping("/admin")
public class AdminAction {

	@Autowired
	private LoginUserService service;
	
	@RequestMapping("/test")
	@ResponseBody
	public String admin() {
		service.test();
		return "ok";
	}
}

MyRealm.java
package com.hqyj.shiro;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import com.hqyj.bean.LoginUser;
import com.hqyj.bean.UserRole;
import com.hqyj.service.LoginUserService;

public class MyRealm extends AuthorizingRealm{
	@Autowired
	private LoginUserService service;
	
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
		LoginUser loginUser = new LoginUser();
		loginUser.setUsername((String) arg0.getPrimaryPrincipal());
		List<UserRole> role_list = service.findRole(loginUser);
		Set<String> set = new HashSet<>();
		for (UserRole userRole : role_list) {
			set.add(userRole.getRolename());
		}
		
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(set);
		return info;
	}

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
		UsernamePasswordToken token = (UsernamePasswordToken) arg0;
		LoginUser user = new LoginUser();
		user.setUsername(token.getUsername());
		user.setPassword(new SimpleHash("MD5", token.getPassword(), token.getUsername(), 1024).toString());
		LoginUser u = service.findBynameandPwd(user);
		if(u==null) {
			throw new AuthenticationException();
		}
		AuthenticationInfo info = new SimpleAuthenticationInfo(token.getUsername(),token.getPassword(),getName());		
		return info;
	}

}

index.jsp(默认进入页面)
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!--  
	参考servlet中的接口:

	request.getScheme();
	返回的协议名称,默认是http

	request.getServerName()
	返回的是服务器地址,如果是本机,则是localhost

	getServerPort()
	获取服务器端口号

	request.getContextPath()得到项目的名字,

-->
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
%>
<!DOCTYPE html>
<html>
<head>
<base href="<%=basePath%>">
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	网站首页
	
	<shiro:guest>
		亲,请<a href="jump?url=login">登录</a> <a href="register">注册</a>
	</shiro:guest>
	<shiro:user>
		欢迎你!<shiro:principal/>
	</shiro:user>
</body>
</html>
login.jsp(登录页面)
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
%>
<!DOCTYPE html>
<html>
<head>
<base href="<%=basePath%>">
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="jquery/jquery-2.1.3.js"></script>
</head>
<body>
<h1>登录</h1>
	<form>
		用户名:<input type="text" name="username" id="username">
		密码:<input type="password" name="password"  id="password">
		<input type="button" value="提交" onclick="check()">
		<a href="register">注册</a>
	</form>
	
<script type="text/javascript">
//检查该用户名是否存在
function check(){
	var username = $("#username").val();
	var password = $("#password").val();
	$.ajax({
		   type: "GET",
		   url: "testlogin",
		   data: "username="+username+"&password="+password,
		   success: function(msg){
			   if(msg=="success"){
				   window.location.href = "success";
				  }else{
					  alert("用户或密码错误");
				  }
		   },
		   error:function(error){
				alert("请求失败");
			}
		   
		});

	
}
</script>	
	
	
</body>
</html>
register.jsp(注册页面)
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<!--  
	参考servlet中的接口:

	request.getScheme();
	返回的协议名称,默认是http

	request.getServerName()
	返回的是服务器地址,如果是本机,则是localhost

	getServerPort()
	获取服务器端口号

	request.getContextPath()得到项目的名字,

-->
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
%>
<!DOCTYPE html>
<html>
<head>
<base href="<%=basePath%>">
<meta charset="UTF-8">
<title>注册页面</title>
<script type="text/javascript" src="jquery/jquery-2.1.3.min.js"></script>
</head>
<body>
<h1>注册</h1>
<form action="registersuccess" onsubmit="return doregister()">
用户名:<input type="text" name="username" onblur="checkusername()" id="username">
<span id="msg"></span>
<br>
密码:<input type="password" name="password" id="password"><br>
<input type="submit" value="注册">
</form>

<script type="text/javascript">
//检查该用户名是否存在
var flag = false;
function checkusername(){
	var username = $("#username").val();
	$.ajax({
		   type: "GET",
		   url: "registeraction",
		   data: "username="+username,
		   success: function(msg){
			   if(msg=="ok"){
					  $("#msg").html("该账号可以使用");
					  flag = true;
				  }else if(msg=="null"){
					  $("#msg").html("请输入账号");
					  flag = false;
				  }else{
					  $("#msg").html("该账号已被占用");
					  flag = false;
				  }
		   	console.log(flag);
		   },
		   error:function(error){
				alert("请求失败");
				 flag = false;
			}   
		});
}

function doregister(){
	if(!flag){
		alert("请输入正确的信息");
	}else{
		alert("注册成功,请登录");
	}
	return flag;
}

</script>



</body>
</html>
success.jsp(登录成功页面)
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

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

<!--  
	参考servlet中的接口:

	request.getScheme();
	返回的协议名称,默认是http

	request.getServerName()
	返回的是服务器地址,如果是本机,则是localhost

	getServerPort()
	获取服务器端口号

	request.getContextPath()得到项目的名字,

-->
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
%>
<!DOCTYPE html>
<html>
<head>
<base href="<%=basePath%>">
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="jquery/jquery-2.1.3.min.js"></script>
</head>
<body>
<h1>欢迎您!
<shiro:principal/>
</h1>

<a href="loginout">注销</a>

<shiro:hasAnyRoles name="admin,test">
<a href="login">主页</a>
</shiro:hasAnyRoles>

<shiro:lacksRole name="admin">
不是管理员看的模板
</shiro:lacksRole>

<shiro:hasRole name="admin">
<a href="javascript:void(0)" onclick="admin()">管理员</a>
</shiro:hasRole>

<script type="text/javascript">
function admin(){
	$.ajax({
		type:"GET",
		url:"admin/test",
		success:function(msg){
			if(msg=="sorry"){
				alert("对不起,您没有权限");
			}else{
				window.location.href="jump?url=admin";
			}
		},
		error:function(msg){
			alert("请求失败");
		}
	});
}

</script>

</body>
</html>
unauthorized.jsp(没有权限跳转的页面)
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<!--  
	参考servlet中的接口:

	request.getScheme();
	返回的协议名称,默认是http

	request.getServerName()
	返回的是服务器地址,如果是本机,则是localhost

	getServerPort()
	获取服务器端口号

	request.getContextPath()得到项目的名字,

-->
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
%>
<!DOCTYPE html>
<html>
<head>
<base href="<%=basePath%>">
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	对不起,你不具备该权限
</body>
</html>
admin.jsp(有权限跳转的页面)
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<!--  
	参考servlet中的接口:

	request.getScheme();
	返回的协议名称,默认是http

	request.getServerName()
	返回的是服务器地址,如果是本机,则是localhost

	getServerPort()
	获取服务器端口号

	request.getContextPath()得到项目的名字,

-->
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
%>
<!DOCTYPE html>
<html>
<head>
<base href="<%=basePath%>">
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	管理员页面
</body>
</html>
启动
主页(配置了过滤器,未认证自动跳转)

在这里插入图片描述

登录(配置了shiro,认证出错提示)

在这里插入图片描述

登录成功(laowang具有admin权限,所以可以看到管理员这个模块)

在这里插入图片描述

因为laowang具有admin权限,所以可以直接访问到admin/test请求

在这里插入图片描述

单击注销回到主页(session中不再有用户信息),有laowang的信息展示

在这里插入图片描述

使用laotie账号登录(不具有admin权限,看不到管理员模块)

在这里插入图片描述

也无法请求到admin/test请求

在这里插入图片描述


  • 总结,shiro的简单学习到此结束,如需深究,请参考官方文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值