shiro
权限概述
什么是权限管理
权限管理实现对用户访问系统的控制 用户可以访问而且只能访问自己被授权的资源 只要有用户和密码的系统,权限管理几乎都会出现 举例 给张三赋予“人力资源经理”角色, “人力资源经理”具有“查询员工”、“添加员工”、“修改员工”和“删除员工”权限。 此时张三能够进入系统,则可以进行这些操作;
权限管理
- 认证 对于需要访问控制的资源用户首先经过身份认证 判断一个用户是否为合法用户的处理过程
- 授权 认证通过后用户具有资源的访问权限-方可访问 控制能够访问哪些资源
shiro概述
Apache Shiro是Java的一个安全框架 Shiro是一个强大的简单易用的Java安全框架,主要用来更便捷的认证、授权、加密、会话管理、与Web集成、缓存等 Shiro使用起来小而简单 spring中有spring security ,是一个权限框架,它和spring依赖过于紧密,没有shiro使用简单。 shiro不依赖于spring,shiro不仅可以实现web应用的权限管理,还可以实现c/s系统,分布式系统权限管理, shiro属于轻量框架,越来越多企业项目开始使用shiro.
shiro核心概念
核心类
整体类图
- Authentication 身份认证/登录,验证用户是不是拥有相应的身份;
- Authorization 授权,即权限验证,验证某个已认证的用户是否拥有某个权限;
- Session Manager 会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;
- Cryptography 加密,保护数据的安全性
- Web Support Web支持,可以非常容易的集成到Web环境;
- Caching 缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
- Concurrency shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
- Testing 提供测试支持;
- Run As 允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
- Remember Me 记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
主要概念
Subject
当前的操作用户 可以是人 爬虫 当前跟软件交互的东西 在shiro当中我们可以统称"用户" 在代码的任何地方,你都能轻易的获得Shiro Subject。 一旦获得Subject,你就可以立即获得你希望用Shiro为当前用户做的90%的事情:登录、退、访问会话、执行授权检查等
SecurityManager
SecurityManager则管理所有用户的安全操作 引用了多个内部嵌套安全组件,是Shiro框架的核心 你可以把它看成DispatcherServlet前端控制器。 用于调度各种Shiro框架的服务
Realms
Realms则是用户的信息认证器和用户的权限认证器 执行认证(登录)和授权(访问控制)时,Shiro会从应用配置的Realm中查找很多内容 Realm 可以理解为读取用户信息、角色及权限的 DAO SecurityManager要验证用户身份与权限,那么它需要从Realm获取相应的信息进行比较以确定用户身份是否合法; 可以把Realm看成DataSource,即安全数据源。
Shiro架构
整体架构图
- subject:主体 主体可以是用户也可以是程序,主体要访问系统,系统需要对主体进行认证、授权。
- securityManager:安全管理器 主体进行认证和授权都是通过securityManager进行。
- authenticator: 认证器 主体进行认证最终通过authenticator进行的。
- authorizer: 授权器 主体进行授权最终通过authenticator进行的。
- sessionManager:会话管理 web应用中一般是用web容器对session进行管理,shiro也提供一套session管理的方式。
- sessionDao: 通过sessionDao管理session数据,
- cacheManager: 缓存管理器 主要对session和授权数据进行缓存,比如将授权数据通过cacheManager进行缓存管理,和 ehcache整合对缓存数据进行管理。
- realm: 领域 相当于数据源,通过realm存取认证、授权相关数据。
- cryptography: 密码管理 提供了一套加密/解密的组件,方便开发。比如 提供常用的散列、加/解密等功能。
认证
什么是认证
身份认证,就是判断一个用户是否为合法用户的处理过程 通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确
关键对象
- Subject:主体 用户
- Principal:身份信息 是主体(subject)进行身份认证的标识,标识必须具有唯一性,如用户名、手机号、邮箱地址等
- credential:凭证信息 是只有主体自己知道的安全信息,如密码、证书等。
使用ini完成认证
- 在Maven中添加依赖jar包
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
- 添加shiro.ini配置文件 首先准备一些用户身份/凭据(shiro.ini):
[users]
itlike=1234
my=1234
- 登录与退出
代码语言:javascript复制1.构建securityManager工厂 2.通过工厂创建securityManager 3.将securityManager设置到运行环境中 4.创建一个Subject实例 5.创建token令牌 6.用户登录 7.用户退出
/*1.构建securityManager工厂*/
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
/*2.通过工厂创建securityManager*/
SecurityManager securityManager = factory.getInstance();
/*3.将securityManager设置到运行环境中*/
SecurityUtils.setSecurityManager(securityManager);
/*4.创建一个Subject实例*/
Subject subject = SecurityUtils.getSubject();
/*5.创建token令牌 用户名和密码*/
UsernamePasswordToken token = new UsernamePasswordToken("itlike", "1234");
/*6.用户登陆*/
try {
subject.login(token);
} catch (UnknownAccountException e) {
System.out.println("用户名不存在");
e.printStackTrace();
}catch (IncorrectCredentialsException e){
System.out.println("密码错误");
e.printStackTrace();
}
System.out.println("是否认证" subject.isAuthenticated());
/*7.用户退出*/
subject.logout();
System.out.println("是否认证" subject.isAuthenticated());
认证流程
认证代码执行流程
- 调用subject.login方法进行登录,其会自动委托给securityManager.login方法进行登录;
- securityManager通过Authenticator(认证器)进行认证;
3.Authenticator的实现ModularRealmAuthenticator调用realm从ini配置文件取用户真实的账号和密码
- IniRealm先根据token中的账号去ini中找该账号,如果找不到则给ModularRealmAuthenticator返回null,如果找到则匹配密码,匹配密码成功则认证通过。
- 最后调用Subject.logout进行退出操作。
自定义realm
- 创建一个类继承AuthorizingRealm
- 覆盖doGetAuthenticationInfo方法,在此方法当中数据库获取用户,交有验证器去验证
- 在ini文件当中进行配置
myRealm=MyRealm
securityManager.realms=$myRealm
散列密码
概述
散列算法一般用于生成数据的摘要信息,是一种不可逆的算法 一般适合存储密码之类的数据,常见的散列算法如MD5、SHA等
使用shiro进行散列密码 Md5Hash SimpleHash
代码语言:javascript复制 Md5Hash md5Hash = new Md5Hash("itlike");
System.out.println(md5Hash);
/*通过加盐的方式来对密码进一步保护*/
Md5Hash md5Hash2 = new Md5Hash("itlike","myxq");
System.out.println(md5Hash2);
/*可进行二次散列*/
Md5Hash md5Hash3 = new Md5Hash("itlike","myxq",2);
System.out.println(md5Hash3);
/*使用SimpleHash*/
SimpleHash simpleHash = new SimpleHash(
"md5",
"itlike",
"myxq",
2);
System.out.println(simpleHash);
realm中配置散列 在ini文件当中进行散列
代码语言:javascript复制[main]
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#散列算法
credentialsMatcher.hashAlgorithmName=md5
#散列次数
credentialsMatcher.hashIterations=3
#指定realm
myRealm=com.itlike.MyRealm
#配置散列
myRealm.credentialsMatcher=$credentialsMatcher
#配置自定义散列
securityManager.realms=$myRealm
要保证数据库中的密码是经过散列之后的
授权
什么是授权
授权,即访问控制,控制谁能访问哪些资源。 主体进行身份认证后需要分配权限,方可访问系统的资源,对于某些资源没有权限是无法访问的。
使用ini形式配置权限信息
在ini文件中用户、角色、权限的配置规则 用户名=密码,角色1,角色2… 首先根据用户名找角色,再根据角色找权限,角色是权限集合。
权限字符串的规则
代码语言:javascript复制“资源标识符:操作:资源实例标识符” 对哪个资源的哪个实例具有什么操作 :”是资源/操作/实例的分割符 权限字符串也可以使用*通配符 示例
[users]
#用户itlike的密码是1234,此用户具有role1和role2两个角色
itlike=1234,role1,role2
myxq=1234,role2
[roles]
#角色role1对资源user拥有create、update权限
role1=user:create,user:update
#角色role2对资源user拥有create、delete权限
role2=user:create,user:delete
#角色role3对资源user拥有create权限
role3=user:create
自定义Realm形式权限
代码语言:javascript复制/*获取身份信息*/
Object principal = principals.getPrimaryPrincipal();
/*根据用户名查询该用户的角色和权限*/
List<String> roles = new ArrayList();
roles.add("role1");
roles.add("role2");
List<String> permissions = new ArrayList();
permissions.add("user:create");
permissions.add("user:delete");
/*把角色和权限与subject关联在一起,返回*/
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRoles(roles);
info.addStringPermissions(permissions);
shiro.ini方式使用流程
- 创建maven工程
- 在Maven中添加依赖jar包. shiro核心jar&&依赖jar
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.30</version>
<scope>test</scope>
</dependency>
- 创建shiro.ini文件
- 认证代码
public static void main( String[] args ){
/**
* 1.构建securityManager工厂
* 2.通过工厂创建securityManager
* 3.将securityManager设置到运行环境中
* 4.创建一个Subject实例
* 5.创建token令牌
* 6.用户登录
* 7.用户退出
*/
//1.构建securityManager工厂
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2.通过工厂创建securityManager
SecurityManager securityManager = factory.getInstance();
//3.将securityManager设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
//4.创建一个Subject实例
Subject subject = SecurityUtils.getSubject();
//5.创建token令牌
UsernamePasswordToken token = new UsernamePasswordToken("joker", "1234");
// 6.用户登录
try {
subject.login(token);
}catch (UnknownAccountException e){
/*账号不存在*/
System.out.println("账号不存在");
}catch (IncorrectCredentialsException e){
/*密码错误*/
System.out.println("密码错误");
}
System.out.println("是否认证成功" subject.isAuthenticated());
//7.用户退出
subject.logout();
System.out.println("是否认证成功" subject.isAuthenticated());
}
自定义realm
- 创建一个类继承AuthorizingRealm
- 自定义认证
/*认证AuthenticationInfo*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
/*判断用户名密码是否存在*/
/*获取用户名*/
String username=(String) token.getPrincipal();
/*从数据库中查询密码*/
String name="joker";
String password="1234";
if(!name.equals(username)){
return null;
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password, this.getName());
return info;
}
- 在ini文件配置realm
myRealm=com.dj.myRealm
securityManager.realms=$myRealm
散列密码加盐 配置散列信息
4. 授权 在 shiro-permission.ini配置文件配置
代码语言:javascript复制[users]
#用户itlike的密码是1234,此用户具有role1和role2两个角色
itlike=1234,role1,role2
myxq=1234,role2
[roles]
#角色role1对资源user拥有create、update权限
role1=user:create,user:update
#角色role2对资源user拥有create、delete权限
role2=user:create,user:delete
#角色role3对资源user拥有create权限
role3=user:create
代码语言:javascript复制 System.out.println("是否认证成功" subject.isAuthenticated());
//在认证成功之后才去授权
//判断当前用户是否有某个角色和
System.out.println(subject.hasRole("role1"));
/*判断当前用户是否有多个角色*/
System.out.println(subject.hasAllRoles(Arrays.asList("role1", "role2")));
/*判断使用有某个权限*/
System.out.println(subject.isPermitted("user:update"));
/*判断是否具备多个权限*/
System.out.println(subject.isPermittedAll("user:create", "user:delete"));
自定义Realm形式权限
代码语言:javascript复制 /*授权AuthorizationInfo*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
/*获取身份信息*/
Object principal = principals.getPrimaryPrincipal();
/*根据用户名查询该用户的角色和权限*/ //从数据库查
List<String> roles = new ArrayList();
roles.add("role1");
roles.add("role2");
List<String> permissions = new ArrayList();
permissions.add("user:create");
permissions.add("user:delete");
/*把角色和权限与subject关联在一起,返回*/
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRoles(roles);
info.addStringPermissions(permissions);
return info;
}
SSM整合shiro
- 添加依赖
<dependencies>
<!--Junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!--Servlet - JSP -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--Mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.14</version>
</dependency>
<!--Spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<!-- joda-time -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.6</version>
</dependency>
<!--shiro-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.24</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
</dependencies>
- 在web.xml当中配置过滤器拦截所有请求,进行处理
<!-- 拦截到所有请求,使用spring一个bean来进行处理 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<!-- 是否filter中的init和 destroy-->
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
web.xml
代码语言:javascript复制<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0"
metadata-complete="false">
<absolute-ordering/>
<display-name>web</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!--配置前端控制器-->
<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*:applicationContext.xml</param-value>
</init-param>
<!-- 项目启动就加载框架 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 拦截到所有请求,使用spring一个bean来进行处理 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<!-- 是否filter中的init和 destroy-->
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
application-mvc.xml
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/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/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
">
<mvc:annotation-driven />
<!--静态资源处理 -->
<mvc:default-servlet-handler/>
<!--视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp" />
</bean>
</beans>
application-mybatis.xml
代码语言:javascript复制<?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.xsd
">
<!--spring-Mybatis整合-->
<!--加载数据库属性文件-->
<context:property-placeholder location="classpath:db.properties"/>
<!--连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}" />
<!--属性文件当中的名称不能和name名称一样-->
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 开启注解事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- Mybatis的工厂 -->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 核心配置文件的位置 -->
<property name="configLocation" value="classpath:sqlMapConfig.xml"/>
<!--配置mapper映射文件的路径-->
<property name="mapperLocations" value="classpath*:com/dj/mapper/*mapper.xml"/>
</bean>
<!-- 配置Mapper接口扫描 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 配置Mapper扫描包 -->
<property name="basePackage" value="com.dj.mapper" />
</bean>
</beans>
applicationContext.xml
代码语言:javascript复制<?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.xsd
">
<!--注解扫描-->
<context:component-scan base-package="com.dj"/>
<!--导入mybatis-->
<import resource="classpath:application-mybatis.xml"/>
<!--导入springMVC-->
<import resource="classpath:application-mvc.xml"/>
<!--导入shiro-->
<import resource="classpath:application-shiro.xml"/>
</beans>
db.properties
代码语言:javascript复制jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/permission?characterEncoding=utf-8
jdbc.username=root
jdbc.password=123456
sqlMapConfig.xml
代码语言:javascript复制<?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="logImpl" value="STDOUT_LOGGING"/>
</settings>-->
<typeAliases>
<!-- 批量别名定义,扫描整个包下的类,别名为类名(大小写不敏感) -->
<package name="com.dj.domain" />
</typeAliases>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
</plugin>
</plugins>
</configuration>
- 在spring当中配置shiro过滤器和安全管理器 application-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:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
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/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--表单过滤器-->
<bean id="MyFormFilter" class="com.dj.web.filter.MyFormFilter"></bean>
<!-- 配置shiro过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"></property>
<!-- 配置登录认证的路径 -->
<property name="loginUrl" value="/login" />
<!--配置表单监听过滤器-->
<property name="filters">
<map>
<entry key="authc" value-ref="MyFormFilter"/>
</map>
</property>
<!-- 配置shiro过滤器pattern -->
<property name="filterChainDefinitions">
<value>
/static/** = anon <!--不需要登录验证-->
/login.jsp = anon <!--不需要登录验证-->
/logout=logout <!--取消认证-->
/**=authc <!--除指定请求外,其它所有的请求都需要身份验证-->
</value>
</property>
</bean>
<!-- 凭证匹配器 -->
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- 散列算法 -->
<property name="hashAlgorithmName" value="md5"/>
<!-- 散列次数 -->
<property name="hashIterations" value="2"></property>
</bean>
<!--自定义realm-->
<bean id="employeeRealm" class="com.dj.web.realm.EmplyeeRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>
<!-- 配置shiro安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--注入realm-->
<property name="realm" ref="employeeRealm"></property>
<property name="cacheManager" ref="ehCache"/>
</bean>
<!-- <aop:config proxy-target-class="true">-->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<!-- 缓存管理器 -->
<bean id="ehCache" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
</bean>
</beans>
- 配置登录认证的路径
<!-- 配置登录认证的路径 -->
<property name="loginUrl" value="/login" />
5. 创建自定义realm
代码语言:javascript复制 /*认证*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("来到了认证");
String username=(String)token.getPrincipal();
System.out.println(username);
/*到数据中查询有没有当前用户*/
employee employee = service.getEmployeeWithUsername(username);
System.out.println(employee);
if(employee==null){
return null;
}
/*认证*/
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(employee,employee.getPassword(),this.getName());
return info;
- 配置realm
- 创建表单提交的过滤器 继承FormAuthenticationFilter
public class MyFormFilter extends FormAuthenticationFilter {
/*认证成功会调用*/
@Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
response.setCharacterEncoding("UTF-8");
/*响应给浏览器*/
System.out.println("认证成功");
AjaxRes ajaxRes = new AjaxRes(true, "登录成功");
/*把对象转成json字符串*/
String jsonString = new ObjectMapper().writeValueAsString(ajaxRes);
response.getWriter().print(jsonString);
return false;
}
/*认证失败 会调用*/
@Override
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
/*响应给浏览器*/
System.out.println("认证失败");
return false;
}
}
- 配置表单监听过滤器
- 跳转首页
- 认证失败
/*认证失败 会调用*/
@Override
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
/*响应给浏览器*/
response.setCharacterEncoding("UTF-8");
if(e!=null){
/*获取异常名称*/
String name = e.getClass().getName();
AjaxRes ajaxRes=null;
if(name.equals(UnknownAccountException.class.getName())){
/*账号不存在*/
ajaxRes = new AjaxRes(false, "账号不存在");
}else if(name.equals(IncorrectCredentialsException.class.getName())){
/*密码错误*/
ajaxRes = new AjaxRes(false, "密码错误");
}else {
/*未知异常*/
ajaxRes = new AjaxRes(false, "未知异常");
}
String jsonString = null;
try {
jsonString = new ObjectMapper().writeValueAsString(ajaxRes);
response.getWriter().print(jsonString);
} catch (IOException ex) {
ex.printStackTrace();
}
}
return false;
}
11. 设置注销url
12. 授权注解
配置支持shiro注解
代码语言:javascript复制<!--
配置为true即使用cglib继承的方式,
false为jdk的接口动态代理 控制器没有实现接口
-->
<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>
- 授权realm
/*授权*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
System.out.println("来到了授权");
employee employee = (employee)principal.getPrimaryPrincipal();
List<String> roles=service.getroleById(employee.getId());
List<String> permission=service.getpermission(employee.getId());
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRoles(roles);
info.addStringPermissions(permission);
return info;
}
- 授权标签
<shiro:hasPermission name=""></shiro:hasPermission>
<shiro:hasRole name=""></shiro:hasRole>
- 散列加密
<!-- 凭证匹配器 -->
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- 散列算法 -->
<property name="hashAlgorithmName" value="md5"/>
<!-- 散列次数 -->
<property name="hashIterations" value="2"></property>
</bean>
<!--自定义realm-->
<bean id="employeeRealm" class="com.dj.web.realm.EmplyeeRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>
- 权限缓存 shiro-ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
shiro.xml
17. 菜单权限
代码语言:javascript复制/*获取树形菜单数据*/
@RequestMapping("/getTreeData")
@ResponseBody
public List<menu> getTreeData(){
List<menu> treeData = service.getTreeData();
System.out.println(treeData);
/*判断权限
如果没有就移除
* */
/*获取用户*/
Subject subject = SecurityUtils.getSubject();
/*当前用户*/
employee employee =(employee) subject.getPrincipal();
if(!employee.getAdmin()){
/*不是管理员 校验权限*/
chePermission(treeData);
}
return treeData;
}
/*校验权限*/
public void chePermission(List<menu> menus){
Subject subject = SecurityUtils.getSubject();
/*遍历所有菜单 及子菜单*/
Iterator<menu> iterator = menus.iterator();
while (iterator.hasNext()) {
menu m = iterator.next();
if(m.getPermission()!=null){
/*判断当前菜单是否有权限 没有就移除*/
String presources = m.getPermission().getPresources();
if(!subject.isPermitted(presources)){
/*没有 从菜单集合 移除*/
iterator.remove();
continue;
}
}
/*判断是否有子菜单 如果有 继续做校验*/
if(m.getChildren().size()>0){
chePermission(m.getChildren());
}
}
}