SpringMVC 和 SpringBoot 整合 Shiro 实现认证与授权

SpringBoot 专栏收录该内容
9 篇文章 0 订阅

1、Shiro 框架简介

什么是 Shiro Apache

Shiro 是一个强大且易用的 Java 安全框架,执行身份验证、授权、密码学和会话管理,它是一个开 源的项目。 Spring security 也是一个安全管理框架,原名(acegi),它依赖于 spring 框架。而 shiro 是一个不依赖于 spring 的轻量级框架,既可以用于 web 应用安全管理,也可以用于 c/s 模式,分布式管理等,越来越多的 企业级项目开始使用 shiro 框架。

Shiro 架构组件

Shiro 组件简介

Subject 组件

Subject 即用户主体,外部应用与 subject 进行交互,subject 记录了当前操作用户,将用 户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的 程序。Subject 在 shiro 中是一个接口,接口中定义了很多认证授权相关的方法,外部程序通过 subject 进行认证授权,而 subject 是通过 SecurityManager 安全管理器进行认证授权。

SecurityManager 组件

SecurityManager 即安全管理器,对全部的 subject 进行安全管理,它是 shiro 的核心, 负责对所有的 subject 进行安全管理。通过 SecurityManager 可以完成 subject 的认证、授权 等。SecurityManager 是一个接口,继承了 Authenticator, Authorizer, SessionManager 三个接口, 通过 Authenticator 接口进行认证,通过 Authorizer 接口进行授权,通过 SessionManager 接 口进行会话管理等。

Authenticator 组件

Authenticator 即认证器,对用户身份进行认证,Authenticator 是一个接口,shiro 提供 ModularRealmAuthenticator 实现类,通过 ModularRealmAuthenticator 基本上可以满足大多数 需求,也可以自定义认证器。

Authorizer 组件

Authorizer 即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用 户是否有此功能的操作权限。

Realm 组件

Realm 即领域,相当于 datasource 数据源,SecurityManager 进行安全认证需要通过 Realm 获取用户权限数据,比如:如果用户身份数据在数据库,那么 Realm 就需要从数据库获取用户身份信息。 注意:不要把 realm 理解成只是从数据源取数据,在 realm 中还有认证授权校验的相关的代码。

SessionManager 组件

sessionManager 即会话管理,shiro 框架定义了一套会话管理,它不依赖 web 容器的 session,所以 shiro 可以使用在非 web 应用上,也可以将分布式应用的会话集中在一点管理, 此特性可使它实现单点登录。

SessionDAO 组件

SessionDAO 即会话 dao,是对 session 会话操作的一套接口,比如要将 session 存储到数据库,可以通过 JDBC 将会话存储到数据库。

CacheManager 组件

CacheManager 即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。

Cryptography 组件

Cryptography 即密码管理,shiro 提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。

Shiro 的 jar 包依赖

  • shiro-core:是核心包,也是使用 shiro 框架的必须包。
  • shiro-web:主要用于整合 web 项目。
  • shiro-spring:主要用于整合 spring 框架。
  • shiro-quartz:主要用于整合任务调度 quartz。
  • shiro-ehcache: 主要用于整合 ehcache 缓存等。
  • shiro-all:可以一次性加入所有 shiro 支持的功能的 jar 包。

2、Shiro 框架实现认证

认证流程

原理分析

  1. 通过加载 shiro.ini 配置文件创建 securityManager。
  2. 调用 subject.login 方法主体提交认证,提交 token 封装的用户信息。
  3. securityManager 进行认证,securityManager 最终由 ModularRealmAuthenticator 进行认 证。
  4. ModularRealmAuthenticator 调用 IniRealm(给 realm 传入 token) 去 ini 配置文件中查询用 户信息。
  5. IniRealm 根据输入的 token(UsernamePasswordToken)从 shiro-first.ini 查询用户 信息 ,根据 账号查 询用户信 息(账 号和密 码),如 果查询 到用户信息,就给 ModularRealmAuthenticator 返回用户信息(账号和密码),如果查询不到,就给 ModularRealmAuthenticator 返回 null。
  6. ModularRealmAuthenticator 接收 IniRealm 返回 Authentication 认证信息,如果返回的认证信息是 null,ModularRealmAuthenticator 抛出异常 (org.apache.shiro.authc.UnknownAccountException),如果返回的认证信息不是 null(说明 inirealm 找到了用户),对 IniRealm 返回用户密码 (在 ini 文件 中存在)和 token 中的密码 进行对比,如果不一致抛出异常 (org.apache.shiro.authc.IncorrectCredentialsException)
  7. 简单而言,根据用户的 token 封装的用户名密码信息和 ini 配置文件中的信息进行对比, 如果有完全一致的信息,则登录验证成功,如果没有,抛出异常则登录验证失败。

Shiro 认证中常见的异常

  • UnknownAccountException 账号不存在异常
  • IncorrectCredentialsException 密码错误异常
  • DisabledAccountException(帐号被禁用)
  • LockedAccountException(帐号被锁定)
  • ExcessiveAttemptsException(登录失败次数过多)
  • ExpiredCredentialsException(凭证过期)等

自定义 Realm 认证

实际开发过程中,realm 域主要用于数据库的交互,我们可以自定义 realm 类,从数据库中获取信息。
自定义 Realm 的步骤:

  1. 自定义 realm 类继承 AuthorizingRealm 抽象类。
  2. 重写验证方法,连接数据库,获取数据。
  3. 自定义 ini 配置文件。
  4. 通过 Junit 进行测试。

散列加密算法

在实际开发过程中,一般密码都是以密文的形式进行保存的,Shiro 也对数据加密提供了支持。常用的加密算法有:MD5,SHA 等,而 Shiro 框架也专门对这些加密算法提供了实现类。

  1. Md5Hash 类:通过带参构造器可以实现带验证的加密。
Md5Hash md5 = new Md5Hash(password, salt, hashIterations); 
  • 第一参数:原始密码的明文
  • 第二参数:盐值随机数
  • 第三参数:散列次数
  1. SimpleHash 类:通过带参构造可以指定加密方式。
SimpleHash sh = new SimpleHash("md5", password, salt, hashIterations);
  • 第一参数:加密方式
  • 第二参数:原始密码的明文
  • 第三参数:盐值随机数
  • 第四参数:散列次数

散列加密算法实现步骤:

  1. 数据插入到数据库时,先根据加密算法加密后再保存。
  2. 自定义 realm 类,继承 AuthorizingRealm 抽象类。
  3. 重写验证方法,根据用户名查询加密后的用户密码和盐值。
  4. 编写 ini 配置文件,设置匹配器。
  5. 编写测试方法,进行测试。

配置文件 shiro-realm-md5.ini:

[main]
# 定义凭证匹配器 
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
# 定义散列算法
credentialsMatcher.hashAlgorithmName=md5
# 定义散列次数
credentialsMatcher.hashIterations=1
# 将凭证匹配器设置到域
md5AuthRealm=com.shiro.realm.Md5AuthRealm
md5AuthRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$md5AuthRealm

3、Shiro 框架实现授权

授权流程

授权方式

  1. 编程式授权,通过代码判断角色和资源,直接通过 Shiro API 提供的方法进行判断
  2. 注解式授权 @RequiresRoles("admin")
  3. 通过 shiro 的自定义标签进行授权
<shiro:hasRole name="admin">…..</shiro:hasRole>

授权配置文件

存放用户权限的配置文件 shiro-perms.ini:

[users]
#用户admin的密码是admin,此用户具有role1和role2两个角色 admin=admin,role1,role2
test=test,role2

[roles]
#角色role1对资源user拥有create、update权限
role1=user:create,user:update
#角色role2对资源user拥有create、delete权限
role2=user:create,user:delete

配置文件中的内容属于静态的资源配置定义,动态的数据配置需要从数据库表中获取。 user:* :*代表通配符,匹配所有 user 操作权限。

授权代码示例

相关 API:

// 该方法用于用户登录验证是否通过
subject. isAuthenticated()
// 判断当前主体是否属于 role1 角色
subject.hasRole("role1")
// 判断当前主体是否具有多个角色
subject.hasAllRoles(Arrays.asList("role1","role2"));
// 判断当前主体是否具有指定的资源权限 
subject.isPermitted("user:create");
// 判断当前主体是否具有指定的多个资源权限
subject.isPermittedAll("user:create","user:delete")

授权步骤:

  1. 创建测试类和测试方法。
  2. 加载 ini 配置文件,创建 Factory 类对象。
  3. 通过 factory 对象,创建 securityManager 对象实例。
  4. 将 SecurityManager 对象设置到 SecurityUtils 工具类中。
  5. 通过 SecurityUtils 工具类创建 subject 主体对象。
  6. 创建具有用户名和密码的令牌 token 对象,通过 subject 对象调用登录方法进行验证。
  7. 验证通过后,通过 subject 对象调用 hasRole(“”)方法,判断主体是否属于指定角色权限。
  8. 通过 subject 对象调用 isPermitted(“user:create”)方法,与配置文件中的配置进行对比,判断是否具有指定的权限资源。

自定义 realm 授权

自定义 realm 的目的主要是为了访问数据库,操作数据库中的数据进行验证,授权判断。 自定义 realm 操作数据库的步骤:

  1. 创建自定义 realm 类,继承 AuthorizingRealm 类。
  2. 重 写 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection Principals) 授权方法。
  3. 获取用户名,通过用户名获取角色,通过角色获取权限。
  4. 把权限信息添加到 AuthorizationInfo 对象并返回。
  5. 在*.ini 配置文件中配置自定义 realm 类。
  6. 编写测试类,进行测试。

4、SpringMVC 框架整合 Shiro

整合 SpringMVC 框架的步骤

  1. 创建 maven 的 web 项目
  2. 添加 spring,springmvc,mybatis,mysql,shiro 的依赖
  3. 在 web.xml 文件中添加 listener,filter 等配置
  4. 创建 spring,mybatis,shiro 的配置文件
  5. 创建开发用的包,dao,service,entity,controller,mapper 等包
  6. 编写控制器类,jsp 视图页面,进行测试

整合 shiro 框架步骤

  1. 添加 shiro 框架需要的 jar 包,包括 shiro-core,shiro-web,shiro-spring 的关系依赖
  2. 在 web.xml 文件中配置 shiro 的过滤器
  3. 添加 applicationContext-shiro.xml 文件,添加在 web.xml 中 filter 对应的 spring 容器中的 bean
  4. 静态资源处理

shiro 常用的过滤器

过滤器简称对应的 Java 类
anonorg.apache.shiro.web.filter.authc.AnonymousFilter
authcorg.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasicorg.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
permsorg.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
portorg.apache.shiro.web.filter.authz.PortFilter
restorg.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
rolesorg.apache.shiro.web.filter.authz.RolesAuthorizationFilter
sslorg.apache.shiro.web.filter.authz.SslFilter
userorg.apache.shiro.web.filter.authc.UserFilter
logoutorg.apache.shiro.web.filter.authc.LogoutFilter

用户信息验证和权限验证

shiroFilter 过滤器 bean 中添加静态资源属性,用于配置用户信息验证和权限验证。

<!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
<property name="filterChainDefinitions">
	<value>
		<!-- 对静态资源设置匿名访问 -->
		/static/* = anon
		/login.do = anon
		/index.do = authc
		<!-- 请求 logout 地址,shiro 去清除 session -->
		/logout.do = logout
		/system/userManager.do = perms[user:manager]			// 用户具备 user:manager 权限才能访问
		/system/roleManager.do = perms[role:manager]			// 用户具备 role:manager 权限才能访问
		/system/toUserRole.do = perms[user:pression_error]
		<!-- /** = authc 所有 url 都必须认证通过才可以访问 -->
		/* = authc
	</value>
</property>
  • anon 过滤器对资源放行,不进行验证
  • authc 过滤器对资源进行验证
  • logout 过滤器用于 shiro 框架的用户退出,销毁当前 session
  • perms 过滤器用于权限验证,具有指定的权限才能访问
  • /* = authc 拦截所有的资源,都要进行验证

连接数据库,获取菜单权限表中的 func_code 字段信息,通过 shiro 框架判断当前用户是否 具备该权限串,如果具备给予操作权限,如果不具备则跳转到系统配置好的无授权页面。

Shiro.xml 配置文件中配置的无授权页面跳转链接:

<!-- 通过 unauthorizedUrl 指定没有权限操作时跳转页面 -->
<property name="unauthorizedUrl" value="/nofunc.jsp" />

基于注解方式的授权

在 springmvc.xml 配置文件中添加 shiro 注解的支持,和 spring aop 代理模式的支持:

<!-- 开启 aop,对类代理 -->
<aop:config proxy-target-class="true"></aop:config>
<!-- 开启 shiro 注解支持 --> 
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
	<property name="securityManager" ref="securityManager"></property>
</bean> 

在控制器上添加权限验证的注解:

@RequiresPermissions(value={"function:manager"})

需要具有 function:manager 的权限,才能执行该 controller 控制器。

@Controller 
public class FunctionController { 
	@RequestMapping("/system/functionManager.do")
	@RequiresPermissions(value={"function:manager"})
	public String functionList(){
		return "system/functionList";
	}
}

基于 JSP 页面标签的授权

JSP 页面添加:

<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro" %>
标签名称标签条件(均是显示标签内容)
<shiro:authenticated>登录之后
<shiro:notAuthenticated>不在登录状态时
<shiro:guest>用户在没有 RememberMe 时
<shiro:user>用户在 RememberMe 时
<shiro:hasAnyRoles name="abc,123" >在有 abc 或者 123 角色时
<shiro:hasRole name="abc">拥有角色 abc
<shiro:lacksRole name="abc">没有角色 abc
<shiro:hasPermission name="abc">拥有权限资源 abc
<shiro:lacksPermission name="abc">没有 abc 权限资源
<shiro:principal>显示用户身份名称
<shiro:principal property="username"/>显示用户身份中的属性值

在 JSP 页面中添加权限验证标签,如果具有该权限则显示,如果不具有则不显示。

退出操作

在 shiro.xml 配置文件中配置退出过滤器,shiro 自动完成系统的退出操作。

<!-- 请求 logout 地址,shiro 去清除 session --> 
/logout.do = logout

在 JSP 页面中直接调用 logout.do 即可销毁 session 退出系统。

shiro 集成缓存管理

缓存的流程

shiro 中提供了对认证信息和授权信息的缓存。shiro 默认是关闭认证信息缓存的,对于授权 信息的缓存 shiro 默认开启的。因为授权信息的数据量比较大,所以我们要对授权信息使用缓存。
shiro 每次授权都会通过 realm 获取权限信息,为了提高访问速度需要添加缓存,第一次从 realm 中读取权限数据,之后不再读取,这里 Shiro 和 Ehcache 整合。

使用 ehcache 插件

  1. 在 pom.xml 依赖文件中添加 shiro 框架对 ehcache 插件的依赖。
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-ehcache</artifactId>
	<version>${shiro.version}</version>
</dependency>
  1. 在 application-shiro.xml 配置文件中添加 spring 容器对缓存管理器的管理。
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
	<property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml" />
</bean>
  1. 将缓存管理器的 bean 注入到安全管理器中
<!-- securityManager 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
	<property name="realm" ref="authenRealm" />
	<property name="cacheManager" ref="cacheManager"/>
</bean>
  1. 在资源文件夹下添加 shiro-ehcache.xml 配置文件,对缓存进行设置。
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
		<!--diskStore:缓存数据持久化的目录 地址 -->
		<diskStore path="E:\workspace\ehcache" />
		<defaultCache maxElementsInMemory="1000" 
			maxElementsOnDisk="10000000" 
			eternal="false" 
			overflowToDisk="false" 
			diskPersistent="false" 
			timeToIdleSeconds="120" 
			timeToLiveSeconds="120"
			diskExpiryThreadIntervalSeconds="120" 
			memoryStoreEvictionPolicy="LRU">
		</defaultCache>
</ehcache>

清除缓存

用户正常退出后,shiro 框架会自动清除缓存信息。
高版本的 shiro 框架,用户非正常登陆也会自动清除缓存信息。
当用户权限修改后,用户再次登陆 shiro 会自动调用 realm 从数据库获取权限数据,如果在 修改权限后想立即清除缓存则可以调用 realm 的 clearCache 方法清除缓存。
手动清除缓存:
在自定义 realm 类中定义 clearCached 方法,在 service 类中调用 realm 类中的缓存方法,可 以手动清除缓存。

public void clearCached() { 
	PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals(); 
	super.clearCache(principals); 
}

一般我们在修改权限数据之后,在 service 实现类中注入自定义 realm,调用 Realm 中清除缓 存的方法。

@Service("roleService")
public class RoleServiceImpl implements IRoleService {
	@Autowired 
	public AuthenRealms authenRealms; 
	// 修改权限信息
	public int insertUserRole(UserRole ur) { 
		int result = userRoleDao.insert(ur); 
		//修改授权信息够,调用情况缓存的方法 
		authenRealms.clearCached(); 
		return result; 
	} 
}

SessionManager 管理器

SessionManager 管理器主要用于 shiro 框架的 session 的管理,包括 session 的失效时长,删 除失效的 session 等等,如果项目中需要使用 session 操作数据的话, 可以定义相应的 sessionDao,注入到 SessionManager 管理器,通过 sessionDao 进行相关的 session 数据操作。 在 application-shiro.xml 配置文件中添加 SessionManager 管理器配置:

<bean id= "sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
	<!-- session 的失效时长,单位毫秒 -->
	<property name="globalSessionTimeout" value="10000" />
	<!-- 删除失效的 session -->
	<property name="deleteInvalidSessions" value="true" />
</bean>

在安全管理器 bean 中注入 SessionManager 管理器,对 SessionManager 进行管理。

<!-- securityManager 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> 
	<property name="realm" ref="authenRealm" />
	<property name="cacheManager" ref="cacheManager" />
	<property name="sessionManager" ref="sessionManager" />
</bean>

5、SpringBoot 集成 Thymeleaf 模板引擎和 Shiro

SpringBoot 集成 Thymeleaf 模板

整合的步骤:

  1. 创建 springboot 项目。
  2. 导入 springboot,thymeleaf,mysql,mybatis,druid 依赖。
  3. 在 application.yml 配置文件中,配置数据库连接的信息。
  4. 添加 mybatis 的配置文件和映射文件。
  5. 添加开发用的自定义包,controller,dao,entity,service,impl,util。
  6. 编写控制器和 html 页面,进行测试。

6、单元测试 Demo 实现 Shiro 认证与授权

## pom.xml

<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>shiro1</groupId>
	<artifactId>shiro1</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>shiro1</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>

		<!-- 添加shiro核心包依赖 -->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-core</artifactId>
			<version>1.3.2</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.2</version>
		</dependency>

	</dependencies>
</project>
## shiro.ini

[users]
admin=admin
## shiro-ream.ini

[main]
myRealm=com.demo.realm.MyRealm
securityManager.realms=$myRealm
## shiro-md5.ini

[main]
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#定义散列算法
credentialsMatcher.hashAlgorithmName=md5
#定义散列次数
credentialsMatcher.hashIterations=1
#将凭证匹配器设置到域
md5AuthRealm=com.demo.realm.Md5AuthRealm
md5AuthRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$md5AuthRealm
## shiro-perm.ini

[users]
#用户admin的密码是admin,此用户具有role1和role2两个角色
admin=admin,role1,role2
test=test,role2

[roles]
#角色role1对资源user拥有create、update权限
role1=user:create,user:update
#角色role2对资源user拥有create、delete权限
role2=user:create,user:delete
## shiro-perm-realm.ini

[main]
authRealm=com.demo.realm.MyPermRealm
securityManager.realms=$authRealm
## Md5AuthRealm.java

package com.demo.realm;

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.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

public class Md5AuthRealm extends AuthorizingRealm {

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		// TODO Auto-generated method stub
		return null;
	}
	/**
	 * 认证方法
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		//通过token对象获取用户账号,用户自己输入的账号
		String username = token.getPrincipal().toString();
		//根据获取到的用户账号查询数据表中对应密码和盐值  加密后的密码值
		String password = "50317b958ee25a1e14449aeb95db5245";
		String salt = "abcd";
		if(password == null || "".equals(password)){
			return null;
		}
		//封装到SimpleAuthenticationInfo实现类对象中
		SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username,password,ByteSource.Util.bytes(salt),"md5AuthRealm");
		return authenticationInfo;
	}
}
## MyPermRealm.java

package com.demo.realm;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

public class MyPermRealm extends AuthorizingRealm {

	/**
	 * 授权操作
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		// 获取用户名
		String username = principals.getPrimaryPrincipal().toString();
		System.out.println("username"+username);
		List<String> perms = new ArrayList<String>();
		//根据用户名从数据库中获取角色信息
		String[] roles = {"role1","role2"};
		//遍历角色,根据角色获取每一个角色具有权限信息
		for(String role : roles){
			System.out.println(role);
			//获取权限信息,并且保存到权限列表中
			perms.add("user:create");
			perms.add("user:delete");
			perms.add("user:update");
		}
		//把权限列表封装到SimpleAuthorizationInfo对象中
		SimpleAuthorizationInfo saif = new SimpleAuthorizationInfo();
		saif.addStringPermissions(perms);
		saif.addRoles(Arrays.asList(roles));
		return saif;
	}

	/**
	 * 认证方法
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		String username = token.getPrincipal().toString();
		//根据用户获取用户密码
		String password = "admin"; //从数据库中获取到的密码的值
		//把用户名和密码封装到AuthenticationInfo的实现类对象中
		SimpleAuthenticationInfo shinfo = new SimpleAuthenticationInfo(username,password,"authRealm");
		return shinfo;
	}
}
## ShiroTest.java

package com.demo;

import java.util.Arrays;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.Factory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;

public class ShiroTest {

	@Test
	public void loginTest() {
		// 认证操作
		// 加载资源文件
		Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:config/shiro.ini");
		// 创建安全管理器
		SecurityManager securityManager = factory.getInstance();
		// 把安全管理器设置到SecurityUtils
		SecurityUtils.setSecurityManager(securityManager);
		// 通过安全工具类创建subject主体对象
		Subject subject = SecurityUtils.getSubject();
		// 创建用户认证用的令牌
		UsernamePasswordToken token = new UsernamePasswordToken("admin", "admin");
		try {
			// 通过subject主体的login方法来进行认证
			subject.login(token);
			System.out.println("通过验证,可以登录系统");
		} catch (Exception e) {
			System.out.println("验证失败。。。。");
		}
		// 判断认证结果
		boolean authenticated = subject.isAuthenticated();
		System.out.println(authenticated);
		// 退出系统
		subject.logout();
	}

	@Test
	public void testRealm() {
		// 加载资源文件
		Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:config/shiro-ream.ini");
		// 创建安全管理器
		SecurityManager securityManager = factory.getInstance();
		// 把安全管理器设置到SecurityUtils
		SecurityUtils.setSecurityManager(securityManager);
		// 通过安全工具类创建subject主体对象
		Subject subject = SecurityUtils.getSubject();
		// 创建用户认证用的令牌
		UsernamePasswordToken token = new UsernamePasswordToken("admin", "admin");
		try {
			// 通过subject主体的login方法来进行认证
			subject.login(token);
			System.out.println("通过验证,可以登录系统");
		} catch (Exception e) {
			System.out.println("验证失败。。。。");
			e.printStackTrace();
		}
		// 判断认证结果
		boolean authenticated = subject.isAuthenticated();
		System.out.println(authenticated);
		// 退出系统
		subject.logout();
	}

	/**
	 * md5和sha的加密算法应用
	 */
	@Test
	public void testMd5() {
		// md5加密算法测试
		String password = "admin";
		String salt = "abcd";
		int times = 1;
		Md5Hash md = new Md5Hash(password, salt, times);
		System.out.println(md.toString());

		SimpleHash sh = new SimpleHash("sha", password, salt, times);
		System.out.println(sh.toString());
	}

	@Test
	public void testShiroMd5() {
		// 加载资源文件
		Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:config/shiro-md5.ini");
		// 创建安全管理器
		SecurityManager securityManager = factory.getInstance();
		// 把安全管理器设置到SecurityUtils
		SecurityUtils.setSecurityManager(securityManager);
		// 通过安全工具类创建subject主体对象
		Subject subject = SecurityUtils.getSubject();
		// 创建用户认证用的令牌
		UsernamePasswordToken token = new UsernamePasswordToken("admin", "admin");
		try {
			// 通过subject主体的login方法来进行认证
			subject.login(token);
			System.out.println("通过验证,可以登录系统");
		} catch (Exception e) {
			System.out.println("验证失败。。。。");
			e.printStackTrace();
		}
		// 判断认证结果
		boolean authenticated = subject.isAuthenticated();
		System.out.println(authenticated);
		// 退出系统
		subject.logout();
	}

	@Test
	public void testPerm() {
		// 加载资源文件
		Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:config/shiro-perm.ini");
		// 创建安全管理器
		SecurityManager securityManager = factory.getInstance();
		// 把安全管理器设置到SecurityUtils
		SecurityUtils.setSecurityManager(securityManager);
		// 通过安全工具类创建subject主体对象
		Subject subject = SecurityUtils.getSubject();
		// 创建用户认证用的令牌
		UsernamePasswordToken token = new UsernamePasswordToken("test", "test");
		try {
			// 通过subject主体的login方法来进行认证
			subject.login(token);
			System.out.println("通过验证,可以登录系统");
		} catch (Exception e) {
			System.out.println("验证失败。。。。");
			e.printStackTrace();
		}
		// 判断认证结果
		boolean authenticated = subject.isAuthenticated();
		System.out.println(authenticated);

		// 判断当前主体对象是否属于指定的角色
		boolean hasRole = subject.hasRole("role1");
		System.out.println("admin的输入role1 :" + hasRole);

		// 判断是否具有自定的权限
		boolean permitted = subject.isPermitted("test");
		System.out.println("当前主体具有创建用户的权限:" + permitted);

		// 多角色判断
		boolean flag = subject.hasAllRoles(Arrays.asList("role1", "role2", "role3"));
		System.out.println("当前主体具有role1和role2两个角色:" + flag);
		// 多权限判断
		boolean ppflag = subject.isPermittedAll("user:create", "user:delete");
		System.out.println("ppflag:" + ppflag);
	}

	@Test
	public void testPermRealm() {
		// 加载资源文件
		Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:config/shiro-perm-realm.ini");
		// 创建安全管理器
		SecurityManager securityManager = factory.getInstance();
		// 把安全管理器设置到SecurityUtils
		SecurityUtils.setSecurityManager(securityManager);
		// 通过安全工具类创建subject主体对象
		Subject subject = SecurityUtils.getSubject();
		// 创建用户认证用的令牌
		UsernamePasswordToken token = new UsernamePasswordToken("admin", "admin");
		try {
			// 通过subject主体的login方法来进行认证
			subject.login(token);
			System.out.println("通过验证,可以登录系统");
		} catch (Exception e) {
			System.out.println("验证失败。。。。");
			e.printStackTrace();
		}
		// 判断认证结果
		boolean authenticated = subject.isAuthenticated();
		System.out.println(authenticated);
		if (authenticated) {
			// 判断当前主体对象是否属于指定的角色
			//boolean hasRole = subject.hasRole("role1");
			//System.out.println("admin的输入role1 :" + hasRole);

			// 判断是否具有自定的权限
			boolean permitted = subject.isPermitted("user:create");
			System.out.println("当前主体具有创建用户的权限:" + permitted);

			// 多角色判断
			boolean flag = subject.hasAllRoles(Arrays.asList("role1", "role2"));
			System.out.println("当前主体具有role1和role2两个角色:" + flag);
			// 多权限判断
			boolean ppflag = subject.isPermittedAll("user:create", "user:delete");
			System.out.println("ppflag:" + ppflag);
		}
	}
}

7、SpringMVC 框架整合 Shiro 实现认证与授权

## pom.xml

<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/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>shiro-web</groupId>
	<artifactId>shiro-web</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>shiro-web Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<properties>
		<java.version>1.8</java.version>
		<spring.version>5.0.7.RELEASE</spring.version>
		<aspectj.version>1.8.13</aspectj.version>
		<cglib.version>3.2.6</cglib.version>
		<mybatis.version>3.4.6</mybatis.version>
		<mybatis-spring.version>1.3.2</mybatis-spring.version>
		<mysql.version>5.1.46</mysql.version>
		<c3p0.version>0.9.5.2</c3p0.version>
		<jackson.version>2.9.3</jackson.version>
		<slf4j.version>1.7.25</slf4j.version>
		<shiro.version>1.3.0</shiro.version>
	</properties>
	<dependencies>
		<!-- 缓存依赖 -->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-ehcache</artifactId>
			<version>${shiro.version}</version>
		</dependency>
		<!-- shiro jar包依赖 -->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-core</artifactId>
			<version>${shiro.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-web</artifactId>
			<version>${shiro.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>${shiro.version}</version>
		</dependency>
		<!-- spring 相关开始 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<!-- aop 相关 -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>${aspectj.version}</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>${aspectj.version}</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjtools</artifactId>
			<version>${aspectj.version}</version>
		</dependency>
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib-nodep</artifactId>
			<version>${cglib.version}</version>
		</dependency>
		<!-- jackson 相关 -->
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>${jackson.version}</version>
		</dependency>
		<!-- mybatis 相关 -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>${mybatis.version}</version>
		</dependency>
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>${mybatis-spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.mybatis.generator</groupId>
			<artifactId>mybatis-generator-core</artifactId>
			<version>1.3.5</version>
		</dependency>
		<!-- c3p0 相关 -->
		<dependency>
			<groupId>com.mchange</groupId>
			<artifactId>c3p0</artifactId>
			<version>${c3p0.version}</version>
		</dependency>
		<!-- MySQL 驱动 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>${mysql.version}</version>
		</dependency>
		<!-- hibernate-validator 相关 -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-validator</artifactId>
			<version>5.4.2.Final</version>
		</dependency>
		<!-- 日志相关 -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>${slf4j.version}</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>${slf4j.version}</version>
		</dependency>
		<!-- 单元测试 -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>
		<!-- servlet-api 相关依赖开始 -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.0.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>javax.servlet.jsp-api</artifactId>
			<version>2.3.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
		<dependency>
			<groupId>taglibs</groupId>
			<artifactId>standard</artifactId>
			<version>1.1.2</version>
		</dependency>
		<dependency>
			<groupId>net.sf.json-lib</groupId>
			<artifactId>json-lib</artifactId>
			<version>2.4</version>
			<classifier>jdk15</classifier>
		</dependency>
	</dependencies>
	<build>
		<finalName>shiro-web</finalName>
	</build>
</project>
## web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	id="WebApp_ID" version="3.0">
	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
	<!-- 加载spring的核心配置文件 -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring/applicationContext-*.xml</param-value>
	</context-param>
	<!-- 配置pringmvc的监听器 -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<!-- 添加springmvc的核心控制器 -->
	<servlet>
		<servlet-name>springmvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring/spring-mvc.xml</param-value>
		</init-param>
	</servlet>
	<servlet-mapping>
		<servlet-name>springmvc</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>

	<!-- 配置shiro框架的过滤器 -->
	<filter>
		<filter-name>shiroFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

		<!-- 设置true由servlet容器控制filter的生命周期 -->
		<init-param>
			<param-name>targetFilterLifecycle</param-name>
			<param-value>true</param-value>
		</init-param>
		<!-- 设置spring容器filter的bean id,如果不设置则找与filter-name一致的bean -->
		<init-param>
			<param-name>targetBeanName</param-name>
			<param-value>shiroFilter</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>shiroFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	
	<!-- 添加字符集过滤器,防止乱码 -->
	<filter>
		<filter-name>encoding</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>encoding</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

</web-app>
## spring-mvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc 
		http://www.springframework.org/schema/mvc/spring-mvc.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.xsd
		 http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
	<!-- 扫描基本包 -->
	<context:component-scan base-package="com.demo.controller" />

	<!-- 开启aop,对类代理 -->
	<aop:config proxy-target-class="true"></aop:config>
	<!-- 开启shiro注解支持 -->
	<bean
	class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
		<property name="securityManager" ref="securityManager"></property>
	</bean>

	<!-- 异常统一处理 -->
	<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
		<property name="defaultErrorView" value="index"></property>
		<property name="exceptionAttribute" value="ex"></property>
	</bean>
	<!-- 添加注解驱动 -->
	<mvc:annotation-driven />
	<!-- 视图解析器 -->
	<bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/jsp/" />
		<property name="suffix" value=".jsp" />
	</bean>
	<!-- 将springmvc不能处理的请求交给tomcat -->
	<mvc:default-servlet-handler />

</beans>
## applicationContext-mybatis.xml

<?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:aop="http://www.springframework.org/schema/aop"
	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.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
        ">
    <!-- 添加扫描所有的包 -->    
    <context:component-scan base-package="com.demo.service" /> 
    <!-- 加载资源文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
	<!-- 配置c3p0连接池 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driver}" />
		<property name="jdbcUrl" value="${jdbc.url}" />
		<property name="user" value="${jdbc.user}" />
		<property name="password" value="${jdbc.password}" />
		<!-- 数据源个性化配置,根据实际项目需求进行: -->
		<property name="initialPoolSize" value="3" />
		<property name="minPoolSize" value="3" />
		<property name="maxPoolSize" value="15" />
		<property name="maxConnectionAge" value="28800" />
		<!-- 最大闲置时间 单位秒 设置为6小时,主要目的避免mysql8小时陷阱 -->
		<property name="maxIdleTime" value="21600" />
	</bean>
	<!-- 配置SQLSessionFactoryBean -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- 配置数据源 -->
		<property name="dataSource" ref="dataSource" />
		<!-- 配置Mybatis配置文件位置 -->
		<property name="configLocation" value="classpath:mybatis-config.xml" />
		<!-- 配置映射文件位置,该属性也可以在Mybatis配置文件中进行 -->
		<property name="mapperLocations" value="classpath:mapper/*.xml" />
	</bean>
	<!-- 把sqlSessionFactory自动注入到mapper -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.demo.dao" />
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
	</bean>
	<!-- 配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>
	<!-- 定义事务通知以及事务属性 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="save*" propagation="REQUIRED"/>
			<tx:method name="add*" propagation="REQUIRED"/>
			<tx:method name="create*" propagation="REQUIRED"/>
			<tx:method name="insert*" propagation="REQUIRED"/>
			<tx:method name="update*" propagation="REQUIRED"/>
			<tx:method name="edit*" propagation="REQUIRED"/>
			<tx:method name="modify*" propagation="REQUIRED"/>
			<tx:method name="merge*" propagation="REQUIRED"/>
			<tx:method name="put*" propagation="REQUIRED"/>
			<tx:method name="remove*" propagation="REQUIRED"/>
			<tx:method name="delete*" propagation="REQUIRED"/>
			<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
			<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
			<tx:method name="count*" propagation="SUPPORTS" read-only="true"/>
			<tx:method name="list*" propagation="SUPPORTS" read-only="true"/>
			<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
		</tx:attributes>
	</tx:advice>
	<!-- 定义事务切面 -->
	<aop:config>
		<aop:pointcut expression="execution(* com.demo.service..*.*(..))" id="pc1"/>
		<aop:advisor advice-ref="txAdvice" pointcut-ref="pc1"/>
	</aop:config>
	
	<!-- 开启cglib代理方式 ,当目标类未实现接口时,利用cglib产生代理对象 -->
	<aop:aspectj-autoproxy proxy-target-class="true"/>
	
</beans>
## applicationContext-shiro.xml

<?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:aop="http://www.springframework.org/schema/aop" 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.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
        ">
	<!-- web.xml中shiro的filter对应的bean -->
	<!-- Shiro 的Web过滤器 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager" />
		<!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
		<property name="loginUrl" value="/login.do" />
		<!-- 认证成功统一跳转到first.action,建议不配置,shiro认证成功自动到上一个请求路径 -->
		<property name="successUrl" value="/index.do" />
		<!-- 通过unauthorizedUrl指定没有权限操作时跳转页面 -->
		<property name="unauthorizedUrl" value="/nofunc.jsp" />

		<!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
		<property name="filterChainDefinitions">
			<value>
				<!-- 对静态资源设置匿名访问 -->
				/static/* = anon
				/login.do = anon
				/toLogin.do=anon
				/index.do = authc
				<!-- 请求 logout地址,shiro去清除session -->
				/logout.do = logout
				/userManager.do = perms[user:manager]
				/roleManager.do = perms[role:manager]
				/toUserRole.do = perms[user:pression]
				<!-- /** = authc 所有url都必须认证通过才可以访问 -->
				/* = authc
			</value>
		</property>
	</bean>
	<!-- securityManager安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="authenRealm" />
		<property name="cacheManager" ref="cacheManager" />
		<property name="sessionManager" ref="sessionManager"></property>
	</bean>
	<!-- realm -->
	<bean id="authenRealm" class="com.demo.realm.AuthenRealms">
		<!-- 将凭证匹配器设置到realm中,realm按照凭证匹配器的要求进行散列 -->
		<property name="credentialsMatcher" ref="credentialsMatcher" />
	</bean>
	<!-- 凭证匹配器 -->
	<bean id="credentialsMatcher"
		class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
		<property name="hashAlgorithmName" value="md5" />
		<property name="hashIterations" value="1" />
	</bean>

	<!-- 缓存的配置 -->
	<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
		<property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml" />
	</bean>
	
	<!-- session管理器 -->
	<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
		<!-- 配置session的失效时长,单位是毫秒 -->
		<property name="globalSessionTimeout" value="100000"></property>
		<!-- 删除失效的session -->
		<property name="deleteInvalidSessions" value="true"></property>
	</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"/>
		<setting name="aggressiveLazyLoading" value="false"/>
	</settings>
</configuration>
## jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.user=root
jdbc.password=root
pool.initSize=3
pool.minSize=3
pool.maxSize=15
pool.MaxIdleTime=256800
## shiro-ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
	<!--diskStore:缓存数据持久化的目录 地址  -->
	<diskStore path="E:\workspace\ehcache" />
	<defaultCache 
		maxElementsInMemory="1000" 
		maxElementsOnDisk="10000000"
		eternal="false" 
		overflowToDisk="false" 
		diskPersistent="false"
		timeToIdleSeconds="120"
		timeToLiveSeconds="120" 
		diskExpiryThreadIntervalSeconds="120"
		memoryStoreEvictionPolicy="LRU">
	</defaultCache>
</ehcache>
## log.properties

log4j.rootCategory=INFO, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n

log4j.category.org.springframework.beans.factory=DEBUG
log4j.category.org.springframework=DEBUG
## User.java

package com.demo.entity;

public class User {
    private Integer userId;
    private String userName;
    private String password;
    private String phone;
    private String email;
    private Integer status;
    private String salt;
    private String note;
    private String createTime;
    private String updateTime;
    
    public String getSalt() {
		return salt;
	}
	public void setSalt(String salt) {
		this.salt = salt;
	}
    public Integer getUserId() {
        return userId;
    }
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName == null ? null : userName.trim();
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password == null ? null : password.trim();
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone == null ? null : phone.trim();
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email == null ? null : email.trim();
    }
    public Integer getStatus() {
        return status;
    }
    public void setStatus(Integer status) {
        this.status = status;
    }
    public String getNote() {
        return note;
    }
    public void setNote(String note) {
        this.note = note == null ? null : note.trim();
    }
    public String getCreateTime() {
        return createTime;
    }
    public void setCreateTime(String createTime) {
        this.createTime = createTime;
    }
    public String getUpdateTime() {
        return updateTime;
    }
    public void setUpdateTime(String updateTime) {
        this.updateTime = updateTime;
    }

	@Override
	public String toString() {
		return "User [userId=" + userId + ", userName=" + userName + ", password=" + password + ", phone=" + phone
				+ ", email=" + email + ", status=" + status + ", note=" + note + ", createTime=" + createTime
				+ ", updateTime=" + updateTime + "]";
	}
}
## UserRole.java

package com.demo.entity;

public class UserRole {

    private Integer userId;
    private Integer roleId;

    public Integer getUserId() {
        return userId;
    }
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
    public Integer getRoleId() {
        return roleId;
    }
    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }
}
## Role.java

package com.demo.entity;

public class Role {
    private Integer roleId;
    private String roleName;
    private String note;
    private Boolean system;
    private Integer status;
    private String createTime;
    private String updateTime;
    private boolean flag = false;

	public Role(){}
    public Role(Integer roleId, String roleName, String note, Boolean system, Integer status, String createTime, String updateTime) {
		super();
		this.roleId = roleId;
		this.roleName = roleName;
		this.note = note;
		this.system = system;
		this.status = status;
		this.createTime = createTime;
		this.updateTime = updateTime;
	}

    public boolean isFlag() {
		return flag;
	}
	public void setFlag(boolean flag) {
		this.flag = flag;
	}
    public Integer getRoleId() {
        return roleId;
    }
    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }
    public String getRoleName() {
        return roleName;
    }
    public void setRoleName(String roleName) {
        this.roleName = roleName == null ? null : roleName.trim();
    }
    public String getNote() {
        return note;
    }
    public void setNote(String note) {
        this.note = note == null ? null : note.trim();
    }
    public Boolean getSystem() {
        return system;
    }
    public void setSystem(Boolean system) {
        this.system = system;
    }
    public Integer getStatus() {
        return status;
    }
    public void setStatus(Integer status) {
        this.status = status;
    }
    public String getCreateTime() {
        return createTime;
    }
    public void setCreateTime(String createTime) {
        this.createTime = createTime;
    }
    public String getUpdateTime() {
        return updateTime;
    }
    public void setUpdateTime(String updateTime) {
        this.updateTime = updateTime;
    }
    	
	@Override
	public String toString() {
		return "Role [roleId=" + roleId + ", roleName=" + roleName + ", note=" + note + ", system=" + system
				+ ", status=" + status + ", createTime=" + createTime + ", updateTime=" + updateTime + "]";
	}
}
## RoleFunction.java

package com.demo.entity;

public class RoleFunction {
    private Integer roleId;
    private Integer funcId;
    public Integer getRoleId() {
        return roleId;
    }
    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }
    public Integer getFuncId() {
        return funcId;
    }
    public void setFuncId(Integer funcId) {
        this.funcId = funcId;
    }
}
## Function.java

package com.demo.entity;

import java.util.Date;

public class Function {

    private Integer funcId;
    private String funcName;
    private String funcUrl;
    private String funcCode;
    private Integer parentId;
    private Integer funcType;
    private Integer status;
    private Integer sortNum;
    private Date createTime;
    private Date updateTime;

    public Integer getFuncId() {
        return funcId;
    }
    public void setFuncId(Integer funcId) {
        this.funcId = funcId;
    }
    public String getFuncName() {
        return funcName;
    }
    public void setFuncName(String funcName) {
        this.funcName = funcName == null ? null : funcName.trim();
    }
    public String getFuncUrl() {
        return funcUrl;
    }
    public void setFuncUrl(String funcUrl) {
        this.funcUrl = funcUrl == null ? null : funcUrl.trim();
    }
    public String getFuncCode() {
        return funcCode;
    }
    public void setFuncCode(String funcCode) {
        this.funcCode = funcCode == null ? null : funcCode.trim();
    }
    public Integer getParentId() {
        return parentId;
    }
    public void setParentId(Integer parentId) {
        this.parentId = parentId;
    }
    public Integer getFuncType() {
        return funcType;
    }
    public void setFuncType(Integer funcType) {
        this.funcType = funcType;
    }
    public Integer getStatus() {
        return status;
    }
    public void setStatus(Integer status) {
        this.status = status;
    }
    public Integer getSortNum() {
        return sortNum;
    }
    public void setSortNum(Integer sortNum) {
        this.sortNum = sortNum;
    }
    public Date getCreateTime() {
        return createTime;
    }
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
    public Date getUpdateTime() {
        return updateTime;
    }
    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }

	@Override
	public String toString() {
		return "Function [funcId=" + funcId + ", funcName=" + funcName + ", funcUrl=" + funcUrl + ", funcCode="
				+ funcCode + ", parentId=" + parentId + ", funcType=" + funcType + ", status=" + status + ", sortNum="
				+ sortNum + ", createTime=" + createTime + ", updateTime=" + updateTime + "]";
	}
}
## IUserDao.java

package com.demo.dao;

import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.demo.entity.User;

public interface IUserDao {

	int deleteByPrimaryKey(Integer userId);
	int insert(User record);
	User selectByPrimaryKey(Integer userId);
	List<User> selectAll();
	int updateByPrimaryKey(User record);
	public User login(@Param("username") String username);
	public List<User> findByPage(@Param("page") Integer page, @Param("rows") Integer rows);
	public long totalCount();
	public User findById(@Param("userId") Integer userId);
}
## IUserRoleDao.java

package com.demo.dao;

import com.demo.entity.UserRole;
import java.util.List;
import org.apache.ibatis.annotations.Param;

public interface IUserRoleDao {

    int deleteByPrimaryKey(@Param("userId") Integer userId, @Param("roleId") Integer roleId);
    int insert(UserRole record);
    List<UserRole> selectAll();
    List<UserRole> findByUserId(Integer userId);
	int deleteByUserId(Integer userId);
}
## IRoleDao.java

package com.demo.dao;

import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.demo.entity.Role;

public interface IRoleDao {

    int deleteByPrimaryKey(Integer roleId);
    int insert(Role record);
    Role selectByPrimaryKey(Integer roleId);
    List<Role> selectAll();
    int updateByPrimaryKey(Role record);
    public List<Role> findByUserId(int id);
	List<Role> findByPage(@Param("page")Integer page, @Param("rows")Integer rows);	
	public long totalCount();
}
## IRoleFunctionDao.java

package com.demo.dao;

import com.demo.entity.RoleFunction;
import java.util.List;
import org.apache.ibatis.annotations.Param;

public interface IRoleFunctionDao {

    int deleteByPrimaryKey(@Param("roleId") Integer roleId, @Param("funcId") Integer funcId);
    int insert(RoleFunction record);
    List<RoleFunction> selectAll();
}
## IFunctionDao.java

package com.demo.dao;

import java.util.List;
import com.demo.entity.Function;

public interface IFunctionDao {

    int deleteByPrimaryKey(Integer funcId);
    int insert(Function record);
    Function selectByPrimaryKey(Integer funcId);
    List<Function> selectAll();
    int updateByPrimaryKey(Function record);
    public List<Function> findByRoleIds(List<Integer> list);
	List<Function> findFunctionByUserId(Integer userId);
}
## IUserDao.xml

<?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">
<mapper namespace="com.demo.dao.IUserDao">
  <resultMap id="BaseResultMap" type="com.demo.entity.User">
    <id column="user_id" jdbcType="INTEGER" property="userId" />
    <result column="user_name" jdbcType="VARCHAR" property="userName" />
    <result column="password" jdbcType="VARCHAR" property="password" />
    <result column="phone" jdbcType="VARCHAR" property="phone" />
    <result column="email" jdbcType="VARCHAR" property="email" />
    <result column="salt" jdbcType="VARCHAR" property="salt" />
    <result column="status" jdbcType="INTEGER" property="status" />
    <result column="note" jdbcType="VARCHAR" property="note" />
    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
    <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
  </resultMap>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from tb_users
    where user_id = #{userId,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="com.demo.entity.User">
    insert into tb_users (user_id, user_name, password, 
      phone, email, status, 
      note, create_time, update_time
      )
    values (#{userId,jdbcType=INTEGER}, #{userName,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, 
      #{phone,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR}, #{status,jdbcType=INTEGER}, 
      #{note,jdbcType=VARCHAR}, #{createTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP}
      )
  </insert>
  <update id="updateByPrimaryKey" parameterType="com.demo.entity.User">
    update tb_users
    set user_name = #{userName,jdbcType=VARCHAR},
      password = #{password,jdbcType=VARCHAR},
      phone = #{phone,jdbcType=VARCHAR},
      email = #{email,jdbcType=VARCHAR},
      status = #{status,jdbcType=INTEGER},
      note = #{note,jdbcType=VARCHAR},
      create_time = #{createTime,jdbcType=TIMESTAMP},
      update_time = #{updateTime,jdbcType=TIMESTAMP}
    where user_id = #{userId,jdbcType=INTEGER}
  </update>
  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select user_id, user_name, password, phone, email, status, note, create_time, update_time
    from tb_users
    where user_id = #{userId,jdbcType=INTEGER}
  </select>
  <select id="selectAll" resultMap="BaseResultMap">
    select user_id, user_name, password, phone, email, status, note, create_time, update_time
    from tb_users
  </select>
  
  <!-- 根据用户名密码查询 -->
  <select id="login" resultMap="BaseResultMap">
  	select * from tb_users where user_name = #{username}
  </select>
  <!-- 根据分页查询 -->
  <select id="findByPage" resultMap="BaseResultMap">
  	select * from tb_users limit #{page},#{rows}
  </select>
  <!-- 统计总记录数 -->
  <select id="totalCount" resultType="long">
  	select count(*) from tb_users
  </select>
  <!-- 根据ID查询 -->
  <select id="findById" resultMap="BaseResultMap">
  	select * from tb_users where user_id = #{userId}
  </select>
</mapper>
## IUserRoleDao.xml

<?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">
<mapper namespace="com.demo.dao.IUserRoleDao">
  <resultMap id="BaseResultMap" type="com.demo.entity.UserRole">
    <id column="user_id" jdbcType="INTEGER" property="userId" />
    <id column="role_id" jdbcType="INTEGER" property="roleId" />
  </resultMap>
  <delete id="deleteByPrimaryKey" parameterType="map">
    delete from tb_user_role
    where user_id = #{userId,jdbcType=INTEGER}
      and role_id = #{roleId,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="com.demo.entity.UserRole">
    insert into tb_user_role (user_id, role_id)
    values (#{userId,jdbcType=INTEGER}, #{roleId,jdbcType=INTEGER})
  </insert>
  <select id="selectAll" resultMap="BaseResultMap">
    select user_id, role_id
    from tb_user_role
  </select>
  <select id="findByUserId" resultMap="BaseResultMap">
  	select * from tb_user_role where user_id = #{userId}
  </select>
  <delete id="deleteByUserId">
  	delete from tb_user_role where user_id = #{userId}
  </delete>
</mapper>
## IRoleDao.xml

<?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">
<mapper namespace="com.demo.dao.IRoleDao">
  <resultMap id="BaseResultMap" type="com.demo.entity.Role">
    <id column="role_id" jdbcType="INTEGER" property="roleId" />
    <result column="role_name" jdbcType="VARCHAR" property="roleName" />
    <result column="note" jdbcType="VARCHAR" property="note" />
    <result column="system" jdbcType="BIT" property="system" />
    <result column="status" jdbcType="INTEGER" property="status" />
    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
    <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
  </resultMap>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from tb_roles
    where role_id = #{roleId,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="com.demo.entity.Role">
    insert into tb_roles (role_id, role_name, note, 
      system, status, create_time, 
      update_time)
    values (#{roleId,jdbcType=INTEGER}, #{roleName,jdbcType=VARCHAR}, #{note,jdbcType=VARCHAR}, 
      #{system,jdbcType=BIT}, #{status,jdbcType=INTEGER}, #{createTime,jdbcType=TIMESTAMP}, 
      #{updateTime,jdbcType=TIMESTAMP})
  </insert>
  <update id="updateByPrimaryKey" parameterType="com.demo.entity.Role">
    update tb_roles
    set role_name = #{roleName,jdbcType=VARCHAR},
      note = #{note,jdbcType=VARCHAR},
      system = #{system,jdbcType=BIT},
      status = #{status,jdbcType=INTEGER},
      create_time = #{createTime,jdbcType=TIMESTAMP},
      update_time = #{updateTime,jdbcType=TIMESTAMP}
    where role_id = #{roleId,jdbcType=INTEGER}
  </update>
  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select role_id, role_name, note, system, status, create_time, update_time
    from tb_roles
    where role_id = #{roleId,jdbcType=INTEGER}
  </select>
  <select id="selectAll" resultMap="BaseResultMap">
    select role_id, role_name, note, system, status, create_time, update_time
    from tb_roles
  </select>
  <select id="findByUserId" resultMap="BaseResultMap">
  	select r.* from tb_roles r,tb_user_role ur where r.role_id = ur.role_id and ur.user_id = #{id}
  </select>
  <select id="findByPage" resultMap="BaseResultMap">
  	select * from tb_roles limit #{page},#{rows}
  </select>
  <select id="totalCount" resultType="long">
  	select count(*) from tb_roles;
  </select>
</mapper>
## IRoleFunctionDao.xml

<?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">
<mapper namespace="com.demo.dao.IRoleFunctionDao">
  <resultMap id="BaseResultMap" type="com.demo.entity.RoleFunction">
    <id column="role_id" jdbcType="INTEGER" property="roleId" />
    <id column="func_id" jdbcType="INTEGER" property="funcId" />
  </resultMap>
  <delete id="deleteByPrimaryKey" parameterType="map">
    delete from tb_role_function
    where role_id = #{roleId,jdbcType=INTEGER}
      and func_id = #{funcId,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="com.demo.entity.RoleFunction">
    insert into tb_role_function (role_id, func_id)
    values (#{roleId,jdbcType=INTEGER}, #{funcId,jdbcType=INTEGER})
  </insert>
  <select id="selectAll" resultMap="BaseResultMap">
    select role_id, func_id
    from tb_role_function
  </select>
</mapper>
## IFunctionDao.xml

<?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">
<mapper namespace="com.demo.dao.IFunctionDao">
	<resultMap id="BaseResultMap" type="com.demo.entity.Function">
		<id column="func_id" jdbcType="INTEGER" property="funcId" />
		<result column="func_name" jdbcType="VARCHAR" property="funcName" />
		<result column="func_url" jdbcType="VARCHAR" property="funcUrl" />
		<result column="func_code" jdbcType="VARCHAR" property="funcCode" />
		<result column="parent_id" jdbcType="INTEGER" property="parentId" />
		<result column="func_type" jdbcType="INTEGER" property="funcType" />
		<result column="status" jdbcType="INTEGER" property="status" />
		<result column="sort_num" jdbcType="INTEGER" property="sortNum" />
		<result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
		<result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
	</resultMap>
	<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
		delete from tb_functions
		where func_id = #{funcId,jdbcType=INTEGER}
	</delete>
	<insert id="insert" parameterType="com.demo.entity.Function">
		insert into tb_functions (func_id, func_name, func_url,
		func_code,
		parent_id, func_type,
		status, sort_num, create_time,
		update_time)
		values
		(#{funcId,jdbcType=INTEGER}, #{funcName,jdbcType=VARCHAR},
		#{funcUrl,jdbcType=VARCHAR},
		#{funcCode,jdbcType=VARCHAR},
		#{parentId,jdbcType=INTEGER}, #{funcType,jdbcType=INTEGER},
		#{status,jdbcType=INTEGER}, #{sortNum,jdbcType=INTEGER},
		#{createTime,jdbcType=TIMESTAMP},
		#{updateTime,jdbcType=TIMESTAMP})
	</insert>
	<update id="updateByPrimaryKey" parameterType="com.demo.entity.Function">
		update tb_functions
		set func_name = #{funcName,jdbcType=VARCHAR},
		func_url = #{funcUrl,jdbcType=VARCHAR},
		func_code =
		#{funcCode,jdbcType=VARCHAR},
		parent_id = #{parentId,jdbcType=INTEGER},
		func_type = #{funcType,jdbcType=INTEGER},
		status =
		#{status,jdbcType=INTEGER},
		sort_num = #{sortNum,jdbcType=INTEGER},
		create_time = #{createTime,jdbcType=TIMESTAMP},
		update_time =
		#{updateTime,jdbcType=TIMESTAMP}
		where func_id =
		#{funcId,jdbcType=INTEGER}
	</update>
	<select id="selectByPrimaryKey" parameterType="java.lang.Integer"
		resultMap="BaseResultMap">
		select func_id, func_name, func_url, func_code, parent_id, func_type,
		status, sort_num,
		create_time, update_time
		from tb_functions
		where
		func_id = #{funcId,jdbcType=INTEGER}
	</select>
	<select id="selectAll" resultMap="BaseResultMap">
		select func_id, func_name, func_url, func_code, parent_id, func_type,
		status, sort_num,
		create_time, update_time
		from tb_functions
	</select>
	<select id="findByRoleIds" resultMap="BaseResultMap"
		parameterType="java.util.List">
		select distinct f.* from tb_functions f , tb_role_function rf where
		f.func_id = rf.func_id and rf.role_id in
		<foreach collection="list" item="id" open="(" close=")"
			separator=",">
			#{id}
		</foreach>
		and f.func_type = 0
	</select>
	<select id="findFunctionByUserId" resultMap="BaseResultMap"
		parameterType="int">
		select DISTINCT f.* from tb_users u,tb_user_role ur,tb_roles
		r,tb_functions f,tb_role_function rf where u.user_id = ur.user_id and
		r.role_id = ur.role_id and f.func_id = rf.func_id and r.role_id =
		rf.role_id and u.user_id = #{userId}
	</select>
</mapper>
## IUserService.java

package com.demo.service;

import com.demo.entity.User;

public interface IUserService {

	public User login(String username,String password);	
	public int updateUser(User user);
}
## UserServiceImpl.java

package com.demo.service.impl;

import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import com.demo.dao.IUserDao;
import com.demo.entity.User;
import com.demo.realm.AuthenRealms;
import com.demo.service.IUserService;

@Service
public class UserServiceImpl implements IUserService {

	@Resource
	AuthenRealms authenRealms;
	@Resource
	IUserDao userDao;
	public User login(String username, String password) {
		User user = userDao.login(username);
		return user;
	}
	
	public int updateUser(User user){
		int result = userDao.updateByPrimaryKey(user);
		//调用清除缓存的方法
		authenRealms.clearCache();
		return result;
	}
}
## IFunctionService.java

package com.demo.service;

import java.util.List;
import com.demo.entity.Function;

public interface IFunctionService {
	public List<Function> findByUserId(int userId);
}
## FunctionServiceImpl.java

package com.demo.service.impl;

import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import com.demo.dao.IFunctionDao;
import com.demo.entity.Function;
import com.demo.service.IFunctionService;

@Service
public class FunctionServiceImpl implements IFunctionService {

	@Resource
	IFunctionDao functionDao;
	public List<Function> findByUserId(int userId) {
		return functionDao.findFunctionByUserId(userId);
	}
}
## LoginController.java

package com.demo.controller;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.demo.entity.User;
import com.demo.service.IUserService;

@Controller
public class LoginController {

	@Resource
	IUserService userService;
	
	@RequestMapping("/toLogin")
	public String toIndex(){
		return "login";
	}
	@RequestMapping("/login.do")
	public String login(String username,String password,HttpServletRequest request){
	
//		当用户名称和密码都不为空的情况下,通过用户名和密码进行数据库查询校验
//		if(username != null && !"".equals(username) && password != null && !"".equals(password)){
//			User user = userService.login(username, password);
//			password = EncryptUtil.encryptMD5(password);
//			if(user != null && user.getPassword().equals(password)){
//				//登录成功
//				HttpSession session = request.getSession();
//				session.setAttribute("user",user);
//				request.setAttribute("msg","登录成功!");
//				return "success";
//			}else{
//				request.setAttribute("msg","用户名或密码错误!");
//				return "login";
//			}
//		}else{
//			request.setAttribute("msg","用户名或密码为空!");
//			return "login";
//		}

		//获取当前的主体对象
		Subject subject = SecurityUtils.getSubject();
		
		//创建用户登录验证令牌
		UsernamePasswordToken token = new UsernamePasswordToken(username,password);
		try {
			subject.login(token);
			boolean flag = subject.isAuthenticated();
			if(flag){ //验证通过
				//把用户对象信息放入到session中
				User user = (User)subject.getPrincipal();
				subject.getSession().setAttribute("user",user);
				return "success";
			}else{
				return "login";
			}
		} catch (Exception e) {
			e.printStackTrace();
			return "login";
		}
	}
	@RequestMapping("/index.do")
	public String index(){
		return "index";
	}
	@RequestMapping("/userManager.do")
	public String userManager(){
		return "success";
	}
	@RequestMapping("/roleManager.do")
	public String roleManager(){
		return "success";
	}
	
	@RequiresPermissions(value={"function:manager"})
	@RequestMapping("/functionManager.do")
	public String functionManager() {
		return "success";
	}
	@RequestMapping("/logout.do")
	public String logout(){
		//清除session
		return "login";
	}
	@RequestMapping("/updateUser.do")
	public String updateUser(User user){
		if(user == null){
			user.setUserId(2);
			user.setUserName("张三");
			user.setPassword("123456");
		}
		int result = userService.updateUser(user);
		System.out.println(result);
		return "success";
	}
}
## EncryptUtil.java

package com.demo.util;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * 数据加密工具类型,主要提供了MD5和SHA1加密工具方法
 */
public class EncryptUtil {
	public static final String KEY_SHA = "SHA";
	public static final String KEY_MD5 = "MD5";
	public static final String KEY_SHA1 = "SHA-1";
	private static final String KEY_SHA256 = "SHA-256";
	private static final String KEY_SHA512 = "SHA-512";

	/**
	 * BASE64解码
	 *
	 * @param key 需要解码的密码字符串
	 * @return
	 * @throws Exception
	 */
	public static byte[] decryptBASE64(String key) throws Exception {
		return (new BASE64Decoder()).decodeBuffer(key);
	}

	/**
	 * BASE64编码
	 *
	 * @param key 需要编码的字节数组
	 * @return
	 * @throws Exception
	 */
	public static String encryptBASE64(byte[] key) throws Exception {
		return (new BASE64Encoder()).encodeBuffer(key);
	}

	/**
	 * MD5加密
	 *
	 * @param data 需要加密的字节数组
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptMD5(byte[] data) throws Exception {

		MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);
		md5.update(data);
		return md5.digest();
	}

	/**
	 * SHA加密
	 *
	 * @param data 需要加密的字节数组
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptSHA(String shaType,byte[] data) throws Exception {

		MessageDigest sha = MessageDigest.getInstance(shaType);
		sha.update(data);
		return sha.digest();
	}

	/**
	 * SHA1加密
	 *
	 * @param data 需要加密的字节数组
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptSHA1(byte[] data) throws Exception {
		return encryptSHA(KEY_SHA1, data);
	}
	
	/**
	 * SHA256加密
	 *
	 * @param data 需要加密的字节数组
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptSHA256(byte[] data) throws Exception {
		return encryptSHA(KEY_SHA256, data);
	}
	
	/**
	 * SHA512加密
	 *
	 * @param data 需要加密的字节数组
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptSHA512(byte[] data) throws Exception {
		return encryptSHA(KEY_SHA512, data);
	}

	/**
	 * MD5加密
	 * 
	 * @param content 待加密明文字符串
	 * @param charset 待加密明文字符串采用的字符编码集
	 * @return -返回MD5加密后的字符串
	 */
	public static String encryptMD5(String content, String charset) {
		try {
			byte[] b = encryptMD5(content.getBytes(charset));
			StringBuffer buffer = new StringBuffer();

			for (int i = 0; i < b.length; i++) {
				String shaHex = Integer.toHexString(b[i] & 0xFF);
				if (shaHex.length() < 2) {
					buffer.append(0);
				}
				buffer.append(shaHex);
			}
			return buffer.toString();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "";
	}

	/**
	 * MD5加密算法,该方法采用UTF-8编码集将明文进行转换,如果明文字符串为其他字符编码,<br>
	 * 请采用<code>encryptMD5(String content,String charset)</code>方法
	 * 
	 * @param content 待加密明文字符串
	 * @return -返回加密后密文字符串
	 */
	public static String encryptMD5(String content) {
		return encryptMD5(content, "UTF-8");
	}

	/**
	 * SHA加密算法
	 * 
	 * @param content -待加密明文
	 * @param charset -明文所采用字符编码集
	 * @return
	 */
	public static String encryptSHA(String shaType,String content, String charset) {
		try {
			byte[] b = encryptSHA(shaType,content.getBytes(charset));
			StringBuffer buffer = new StringBuffer();
			// 字节数组转换为 十六进制 数
			for (int i = 0; i < b.length; i++) {
				String shaHex = Integer.toHexString(b[i] & 0xFF);
				if (shaHex.length() < 2) {
					buffer.append(0);
				}
				buffer.append(shaHex);
			}
			return buffer.toString();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "";
	}
	
	/**
	 * SHA加密算法
	 * @param content -待加密明文字符串
	 * @return
	 */
	public static String encryptSHA(String shaType,String content){
		return encryptSHA(shaType,content,"UTF-8");
	}
	
	/**
	 * SHA1加密算法
	 * @param content -待加密明文字符串
	 * @param charset -明文所采用字符编码集
	 * @return
	 */
	public static String encryptSHA1(String content,String charset){
		
		return encryptSHA(KEY_SHA1, content,charset);
	}
	
	/**
	 * SHA1加密算法
	 * @param content -待加密明文字符串
	 * @return
	 */
	public static String encryptSHA1(String content){
		return encryptSHA1(content,"UTF-8");
	}
	
	/**
	 * SHA256加密算法
	 * @param content -待加密明文字符串
	 * @param charset -明文所采用字符编码集
	 * @return
	 */
	public static String encryptSHA256(String content,String charset){
		
		return encryptSHA(KEY_SHA256, content,charset);
	}
	
	/**
	 * SHA256加密算法
	 * @param content -待加密明文字符串
	 * @return
	 */
	public static String encryptSHA256(String content){
		return encryptSHA256(content,"UTF-8");
	}
	
	/**
	 * SHA512加密算法
	 * @param content -待加密明文字符串
	 * @param charset -明文所采用字符编码集
	 * @return
	 */
	public static String encryptSHA512(String content,String charset){
		
		return encryptSHA(KEY_SHA512, content,charset);
	}
	
	/**
	 * SHA512加密算法
	 * @param content -待加密明文字符串
	 * @return
	 */
	public static String encryptSHA512(String content){
		return encryptSHA512(content,"UTF-8");
	}
}
## AuthenRealms.java

package com.demo.realm;

import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;
import org.apache.shiro.SecurityUtils;
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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import com.demo.entity.Function;
import com.demo.entity.User;
import com.demo.service.IFunctionService;
import com.demo.service.IUserService;

public class AuthenRealms extends AuthorizingRealm {

	@Resource
	IUserService userService;
	@Resource
	IFunctionService functionService;
	/**
	 * 授权方法
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		//获取当前用户信息
		User user = (User)principals.getPrimaryPrincipal();
		List<String> perms = new ArrayList<String>();
		//List<String> roles = new ArrayList<String>();
		//根据用户信息获取所属于角色
		
		//根据角色查权限
		List<Function> functions = functionService.findByUserId(user.getUserId());
		for(Function func : functions){
			perms.add(func.getFuncCode());
		}
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		//把所有的权限串列表都放到SimpleAuthorizationInfo对象中
		authorizationInfo.addStringPermissions(perms); 
		//authorizationInfo.addRoles(roles);		//放置所有的角色信息
		
		return authorizationInfo;
	}
	/**
	 * 认证方法
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		//获取用户名名称
		String username = token.getPrincipal().toString();
		//通过用户名获取用户对象
		User user = userService.login(username, null);
		//把user对象封装的AuthenticationInfo中返回
		SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user,user.getPassword(),ByteSource.Util.bytes(user.getSalt()),"authenRealm");
		return authenticationInfo;
	}

	/**
	 * 清除缓存
	 */
	public void clearCache(){
		//获取当前主体对象
		PrincipalCollection  principal = SecurityUtils.getSubject().getPrincipals();
		super.clearCache(principal); //清除缓存
	}
}
## login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登录页面</title>
</head>
<body>	
	<h2>登录页面</h2>
	<form action="login.do" method="post" name="loginForm">
		用户名称:<input type="text" name="username" value=""/><br>
		用户密码:<input type="password" name="password" value=""/><br>
		<input type="submit" name="sub" value="登录"/>
	</form>
	${msg}
	
	${user}
</body>
</html>
## success.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"  pageEncoding="UTF-8"%>
<%@ taglib uri ="http://shiro.apache.org/tags" prefix="shiro"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
	function logoutSys(){
		document.location.href="logout.do";
	}
</script>
</head>
<body>	
	成功页面!
	
	<shiro:authenticated>
		登录后显示的内容!
	</shiro:authenticated>
	
	用户名:<shiro:principal property="userName"></shiro:principal><br>
	密码:<shiro:principal property="password"></shiro:principal><br>
	email:<shiro:principal property="email"></shiro:principal>
	
	<br>
	权限判断:
	<shiro:hasPermission name="function:add">
		<a href="/addFunction.do">添加菜单</a>
	</shiro:hasPermission>
	<shiro:hasPermission name="function:update">
		<a href="/updateFunction.do">修改菜单</a>
	</shiro:hasPermission>
	<shiro:hasPermission name="function:delete">
		<a href="/deleteFunction.do">删除菜单</a>
	</shiro:hasPermission>
	<br>
	<input type="button" name="btn" value="退出" onclick="logoutSys()">
</body>
</html>
## index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"  pageEncoding="UTF-8"%>
<%@ taglib uri ="http://shiro.apache.org/tags" prefix="shiro"%>
<html>
<body>
<h2>Hello World!</h2>
	<shiro:notAuthenticated>没有登录时显示该信息</shiro:notAuthenticated>
	<shiro:authenticated>登录后显示该信息!</shiro:authenticated>
</body>
</html>
## noFunc.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	对不起,您没有操作权限!
</body>
</html>

8、SpringBoot 框架整合 Shiro 实现认证与授权

## pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.6.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>springboot-shiro</groupId>
	<artifactId>springboot-shiro</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>springboot-shiro</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<!-- shiro集成spring -->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>1.3.0</version>
		</dependency>
		<!-- thymeleaf 集成shiro自定义标签 -->
		<dependency>
			<groupId>com.github.theborakompanioni</groupId>
			<artifactId>thymeleaf-extras-shiro</artifactId>
			<version>2.0.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.2</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.1.10</version>
		</dependency>
		<!-- 实现springboot的热部署 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<optional>true</optional>
			<scope>true</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>
## application.yml

# 数据库的配置信息
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource

# mybatis的配置信息
mybatis:
  config-location: classpath:mybatis/config/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml
  
server:
  port: 8080
## 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"/>
		<!-- 主键自动生成 -->
		<setting name="useGeneratedKeys" value="true"/>
	</settings>	
</configuration>
## ShiroConfig.java

package com.demo.config;

import java.util.HashMap;
import java.util.Map;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.demo.realm.MyShiroRealm;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;

@Configuration
public class ShiroConfig {

	//添加创建securityManager的工厂类注入bean
	@Bean
	public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
	
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setSecurityManager(securityManager);		
		Map<String,String> perms = new HashMap<String,String>();
		/**
		 * 设置权限认证过滤器
		 * anon : 可以匿名访问
		 * authc: 必须通过认证,授权后才能访问
		 */
		perms.put("/toLogin.do", "anon");
		perms.put("/userList.do", "authc");		
		perms.put("/toAdd.do", "perms[user:add]");
		perms.put("/toUpdate", "perms[user:update]");
		perms.put("/toDelete", "perms[user:delete]");
		
		shiroFilterFactoryBean.setLoginUrl("/toLogin.do");
		//shiroFilterFactoryBean.setSuccessUrl("login.do");
		shiroFilterFactoryBean.setUnauthorizedUrl("/unFunc.do");		
		//把权限过滤map设置shiroFilterFactoryBean
		shiroFilterFactoryBean.setFilterChainDefinitionMap(perms);
		return shiroFilterFactoryBean;
	}
	
	//创建SecurityManager类的注入bean
	@Bean(name="securityManager")
	public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("shiroRealm") MyShiroRealm shiroRealm){
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(shiroRealm);
		return securityManager;
	}
	
	//创建自定义realm域类的注入bean
	@Bean(name="shiroRealm")
	public MyShiroRealm getMyShiroRealm(){
		MyShiroRealm shiroRealm = new MyShiroRealm();
		return shiroRealm;
	}
	//提供对thymeleaf模板引擎的页面中的shiro自定义标签的支持
	@Bean 
	public ShiroDialect getShiroDialect(){
		return new ShiroDialect();
	}
}
## User.java

package com.demo.entity;

public class User {

    private Integer userId;
    private String userName;
    private String password;
    private String phone;
    private String email;
    private Integer status;
    private String salt;
    private String note;
    private String createTime;
    private String updateTime;

    public String getSalt() {
		return salt;
	}
	public void setSalt(String salt) {
		this.salt = salt;
	}
    public Integer getUserId() {
        return userId;
    }
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName == null ? null : userName.trim();
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password == null ? null : password.trim();
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone == null ? null : phone.trim();
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email == null ? null : email.trim();
    }
    public Integer getStatus() {
        return status;
    }
    public void setStatus(Integer status) {
        this.status = status;
    }
    public String getNote() {
        return note;
    }
    public void setNote(String note) {
        this.note = note == null ? null : note.trim();
    }
    public String getCreateTime() {
        return createTime;
    }
    public void setCreateTime(String createTime) {
        this.createTime = createTime;
    }
    public String getUpdateTime() {
        return updateTime;
    }
    public void setUpdateTime(String updateTime) {
        this.updateTime = updateTime;
    }

	@Override
	public String toString() {
		return "User [userId=" + userId + ", userName=" + userName + ", password=" + password + ", phone=" + phone
				+ ", email=" + email + ", status=" + status + ", note=" + note + ", createTime=" + createTime
				+ ", updateTime=" + updateTime + "]";
	}
}
## Function.java

package com.demo.entity;

import java.util.Date;

public class Function {

    private Integer funcId;
    private String funcName;
    private String funcUrl;
    private String funcCode;
    private Integer parentId;
    private Integer funcType;
    private Integer status;
    private Integer sortNum;
    private Date createTime;
    private Date updateTime;
    
    public Integer getFuncId() {
        return funcId;
    }
    public void setFuncId(Integer funcId) {
        this.funcId = funcId;
    }
    public String getFuncName() {
        return funcName;
    }
    public void setFuncName(String funcName) {
        this.funcName = funcName == null ? null : funcName.trim();
    }
    public String getFuncUrl() {
        return funcUrl;
    }
    public void setFuncUrl(String funcUrl) {
        this.funcUrl = funcUrl == null ? null : funcUrl.trim();
    }
    public String getFuncCode() {
        return funcCode;
    }
    public void setFuncCode(String funcCode) {
        this.funcCode = funcCode == null ? null : funcCode.trim();
    }
    public Integer getParentId() {
        return parentId;
    }
    public void setParentId(Integer parentId) {
        this.parentId = parentId;
    }
    public Integer getFuncType() {
        return funcType;
    }
    public void setFuncType(Integer funcType) {
        this.funcType = funcType;
    }
    public Integer getStatus() {
        return status;
    }
    public void setStatus(Integer status) {
        this.status = status;
    }
    public Integer getSortNum() {
        return sortNum;
    }
    public void setSortNum(Integer sortNum) {
        this.sortNum = sortNum;
    }
    public Date getCreateTime() {
        return createTime;
    }
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
    public Date getUpdateTime() {
        return updateTime;
    }
    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }

	@Override
	public String toString() {
		return "Function [funcId=" + funcId + ", funcName=" + funcName + ", funcUrl=" + funcUrl + ", funcCode="
				+ funcCode + ", parentId=" + parentId + ", funcType=" + funcType + ", status=" + status + ", sortNum="
				+ sortNum + ", createTime=" + createTime + ", updateTime=" + updateTime + "]";
	}
}
## IUserMapper.java

package com.demo.dao;

import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.demo.entity.User;

@Mapper
public interface IUserMapper {

	public User login(String username);
	public List<User> findAll();
}
## IFunctionMapper.java

package com.demo.dao;

import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.demo.entity.Function;

@Mapper
public interface IFunctionMapper {

    int deleteByPrimaryKey(Integer funcId);
    int insert(Function record);
    Function selectByPrimaryKey(Integer funcId);
    List<Function> selectAll();
    int updateByPrimaryKey(Function record);
    public List<Function> findByRoleIds(List<Integer> list);
	List<Function> findFunctionByUserId(Integer userId);
}
## IUserMapper.xml

<?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">
<mapper namespace="com.demo.dao.IUserMapper">
	<resultMap id="BaseResultMap" type="com.demo.entity.User">
    <id column="user_id" jdbcType="INTEGER" property="userId" />
    <result column="user_name" jdbcType="VARCHAR" property="userName" />
    <result column="password" jdbcType="VARCHAR" property="password" />
    <result column="phone" jdbcType="VARCHAR" property="phone" />
    <result column="email" jdbcType="VARCHAR" property="email" />
    <result column="salt" jdbcType="VARCHAR" property="salt" />
    <result column="status" jdbcType="INTEGER" property="status" />
    <result column="note" jdbcType="VARCHAR" property="note" />
    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
    <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
  </resultMap>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from tb_users
    where user_id = #{userId,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="com.demo.entity.User">
    insert into tb_users (user_id, user_name, password, 
      phone, email, status, 
      note, create_time, update_time
      )
    values (#{userId,jdbcType=INTEGER}, #{userName,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, 
      #{phone,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR}, #{status,jdbcType=INTEGER}, 
      #{note,jdbcType=VARCHAR}, #{createTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP}
      )
  </insert>
  <update id="updateByPrimaryKey" parameterType="com.demo.entity.User">
    update tb_users
    set user_name = #{userName,jdbcType=VARCHAR},
      password = #{password,jdbcType=VARCHAR},
      phone = #{phone,jdbcType=VARCHAR},
      email = #{email,jdbcType=VARCHAR},
      status = #{status,jdbcType=INTEGER},
      note = #{note,jdbcType=VARCHAR},
      create_time = #{createTime,jdbcType=TIMESTAMP},
      update_time = #{updateTime,jdbcType=TIMESTAMP}
    where user_id = #{userId,jdbcType=INTEGER}
  </update>
  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select user_id, user_name, password, phone, email, status, note, create_time, update_time
    from tb_users
    where user_id = #{userId,jdbcType=INTEGER}
  </select>
  <select id="findAll" resultMap="BaseResultMap">
    select user_id, user_name, password, phone, email, status, note, create_time, update_time
    from tb_users
  </select>
  <!-- 根据用户名密码查询 -->
  <select id="login" resultMap="BaseResultMap">
  	select * from tb_users where user_name = #{username}
  </select>
  <!-- 根据分页查询 -->
  <select id="findByPage" resultMap="BaseResultMap">
  	select * from tb_users limit #{page},#{rows}
  </select>
  <!-- 统计总记录数 -->
  <select id="totalCount" resultType="long">
  	select count(*) from tb_users
  </select>
  <!-- 根据ID查询 -->
  <select id="findById" resultMap="BaseResultMap">
  	select * from tb_users where user_id = #{userId}
  </select>
</mapper>
## IFunctionMapper.xml

<?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">
<mapper namespace="com.demo.dao.IFunctionMapper">
	<resultMap id="BaseResultMap" type="com.demo.entity.Function">
		<id column="func_id" jdbcType="INTEGER" property="funcId" />
		<result column="func_name" jdbcType="VARCHAR" property="funcName" />
		<result column="func_url" jdbcType="VARCHAR" property="funcUrl" />
		<result column="func_code" jdbcType="VARCHAR" property="funcCode" />
		<result column="parent_id" jdbcType="INTEGER" property="parentId" />
		<result column="func_type" jdbcType="INTEGER" property="funcType" />
		<result column="status" jdbcType="INTEGER" property="status" />
		<result column="sort_num" jdbcType="INTEGER" property="sortNum" />
		<result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
		<result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
	</resultMap>
	<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
		delete from tb_functions
		where func_id = #{funcId,jdbcType=INTEGER}
	</delete>
	<insert id="insert" parameterType="com.demo.entity.Function">
		insert into tb_functions (func_id, func_name, func_url,
		func_code,
		parent_id, func_type,
		status, sort_num, create_time,
		update_time)
		values
		(#{funcId,jdbcType=INTEGER}, #{funcName,jdbcType=VARCHAR},
		#{funcUrl,jdbcType=VARCHAR},
		#{funcCode,jdbcType=VARCHAR},
		#{parentId,jdbcType=INTEGER}, #{funcType,jdbcType=INTEGER},
		#{status,jdbcType=INTEGER}, #{sortNum,jdbcType=INTEGER},
		#{createTime,jdbcType=TIMESTAMP},
		#{updateTime,jdbcType=TIMESTAMP})
	</insert>
	<update id="updateByPrimaryKey" parameterType="com.demo.entity.Function">
		update tb_functions
		set func_name = #{funcName,jdbcType=VARCHAR},
		func_url = #{funcUrl,jdbcType=VARCHAR},
		func_code =
		#{funcCode,jdbcType=VARCHAR},
		parent_id = #{parentId,jdbcType=INTEGER},
		func_type = #{funcType,jdbcType=INTEGER},
		status =
		#{status,jdbcType=INTEGER},
		sort_num = #{sortNum,jdbcType=INTEGER},
		create_time = #{createTime,jdbcType=TIMESTAMP},
		update_time =
		#{updateTime,jdbcType=TIMESTAMP}
		where func_id =
		#{funcId,jdbcType=INTEGER}
	</update>
	<select id="selectByPrimaryKey" parameterType="java.lang.Integer"
		resultMap="BaseResultMap">
		select func_id, func_name, func_url, func_code, parent_id, func_type,
		status, sort_num,
		create_time, update_time
		from tb_functions
		where
		func_id = #{funcId,jdbcType=INTEGER}
	</select>
	<select id="selectAll" resultMap="BaseResultMap">
		select func_id, func_name, func_url, func_code, parent_id, func_type,
		status, sort_num,
		create_time, update_time
		from tb_functions
	</select>
	<select id="findByRoleIds" resultMap="BaseResultMap"
		parameterType="java.util.List">
		select distinct f.* from tb_functions f , tb_role_function rf where
		f.func_id = rf.func_id and rf.role_id in
		<foreach collection="list" item="id" open="(" close=")"
			separator=",">
			#{id}
		</foreach>
		and f.func_type = 0
	</select>
	<select id="findFunctionByUserId" resultMap="BaseResultMap"
		parameterType="int">
		select 
			DISTINCT 
			f.* 
		from 
			tb_users u,tb_user_role ur,tb_roles r,tb_functions f,tb_role_function rf 
		where 
			u.user_id = ur.user_id and
			r.role_id = ur.role_id and 
			f.func_id = rf.func_id and 
			r.role_id =	rf.role_id and 
			u.user_id = #{userId}
	</select>
</mapper>
## IUserService.java

package com.demo.service;

import java.util.List;
import com.demo.entity.Function;
import com.demo.entity.User;

public interface IUserService {

	public User findByUsername(String username);
	public List<User> findAll();
	public List<Function> findFuncByUserId(Integer userId);
}
## UserServiceImpl.java

package com.demo.service.impl;

import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import com.demo.dao.IFunctionMapper;
import com.demo.dao.IUserMapper;
import com.demo.entity.Function;
import com.demo.entity.User;
import com.demo.service.IUserService;
@Service
public class UserServiceImpl implements IUserService {

	@Resource
	IUserMapper userMapper;
	@Resource
	IFunctionMapper functionMapper;
	@Override
	public User findByUsername(String username) {
		return userMapper.login(username);
	}
	@Override
	public List<User> findAll() {
		
		return userMapper.findAll();
	}
	@Override
	public List<Function> findFuncByUserId(Integer userId) {
		return functionMapper.findFunctionByUserId(userId);
	}
}
## LoginController.java

package com.demo.controller;

import java.util.List;
import javax.annotation.Resource;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.demo.entity.User;
import com.demo.service.IUserService;

@Controller
public class LoginController {

	@Resource
	IUserService userService;
	
	@RequestMapping("/toLogin.do")
	public String toLogin() {
		return "login";
	}

	@RequestMapping("/login.do")
	public String login(String username, String password, Model model) {
		// 判断用户名密码是否为空
		if (username != null && !"".equals(username) && password != null && !"".equals(password)) {
			// 进行登录验证
			Subject subject = SecurityUtils.getSubject();
			// 创建验证用的令牌对象
			UsernamePasswordToken token = new UsernamePasswordToken(username, password);
			try {
				subject.login(token);
				boolean flag = subject.isAuthenticated();
				if(flag){
					System.out.println("登录成功!");
					//获取当前用对象,放入到session中
					User user = (User)subject.getPrincipal();
					subject.getSession().setAttribute("user",user);
					return "redirect:userList.do";
				}else{
					model.addAttribute("msg","登录认证失败!");
					return "login";
				}
			
			} catch (Exception e) {
				model.addAttribute("msg","登录认证失败!");
				//e.printStackTrace();
				return "login";
			}
		}
		return "success";
	}
	@RequestMapping("/userList.do")
	public String userList(Model model){
		//查询所有的用户信息并且显示到页面上
		List<User> list = userService.findAll();
		model.addAttribute("userList", list);
		return "userList";
	}
	@RequestMapping("/unFunc.do")
	public String noFunc(){
		return "unFunc";
	}
}
## MyShiroRealm.java

package com.demo.realm;

import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;
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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import com.demo.entity.Function;
import com.demo.entity.User;
import com.demo.service.IUserService;

public class MyShiroRealm extends AuthorizingRealm {

	@Resource
	IUserService userService;
	/**
	 * 授权的方法
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		System.out.println("执行了授权的方法");
		//获取用户对象
		User user = (User)principals.getPrimaryPrincipal();
		//获取用户权限列表
		List<String> perms = new ArrayList<String>();
		//根据用户id获取权限类别
		List<Function> functions = userService.findFuncByUserId(user.getUserId());
		if(functions != null){
			for(Function func : functions){
				perms.add(func.getFuncCode());
			}
		}
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		//把用户的所有权限类别添加到对象中
		authorizationInfo.addStringPermissions(perms); 
		//把所有的用户角色添加到对象中
		//authorizationInfo.addRoles(roles); 
		return authorizationInfo;
	}

	/**
	 * 认证方法
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		System.out.println("执行了认证方法。。。。");
		String username = token.getPrincipal().toString();
		System.out.println("username:" + username);
		//需要通过用户名查询用户密码
		User user = userService.findByUsername(username);
		String password = user.getPassword();
		//把用户名和密码封装到AuthenticationInfo对象中
		SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user,password,"shiroRealm");
		return authenticationInfo;
	}
}
## SpringbootShiroApplication.java

package com.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringbootShiroApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringbootShiroApplication.class, args);
	}
}
## login.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>用户登录页面</title>
</head>
<body>
	<h2>登录页面</h2>
	<form action="login.do" method="post" name="loginForm">
		用户名称:<input type="text" name="username" value=""><br>
		用户密码:<input type="password" name="password" value=""><br>
		<input type="submit" name="log" value="登录"/>
	</form>
	<div th:text="${msg}"></div>
</body>
</html>
## success.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	成功页面!
</body>
</html>
## userList.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>用户列表页面</title>
<style type="text/css">
	table{
		border: 1px solid grey;
		border-collapse: collapse;
	}
	table td{
		border: 1px solid grey;
		border-collapse: collapse;
	}
</style>
</head>
<body>
	<h2>用户列表页面</h2>
	<table>	
		<caption>
			<div shiro:hasPermission="user:add"><a href="/toAdd.do">添加</a></div>
			<div shiro:hasPermission="user:update"><a href="/toUpdate.do">修改</a></div>
			<div shiro:hasPermission="user:delete"><a href="/toDelete.do">删除</a></div>
		</caption>
		<tr>
			<td>用户ID</td><td>用户名称</td><td>电话</td><td>email</td><td>状态</td><td>创建时间</td><td>备注</td>
		</tr>
		<tr th:each="user : ${userList}">
			<td th:text="${user.userId}"></td>
			<td th:text="${user.userName}"></td>
			<td th:text="${user.phone}"></td>
			<td th:text="${user.email}"></td>
			<td th:if="${user.status == 1 ?'正常':'异常'}">正常</td>
			<td th:text="${user.createTime}"></td>
			<td th:text="${user.note}"></td>
		</tr>
	</table>
</body>
</html>
## unFunc.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	对不起,您没有访问权限!
</body>
</html>
  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

打赏
文章很值,打赏犒劳作者一下
相关推荐
©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页

打赏

魏晓蕾

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值