SSM整合Shiro
1. 添加pom依赖
<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>
2. 在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>
3. 新建shiro配置文件并在spring的核心配置文件中引入(文末附有整个shiro.xml)
<!-- 配置shiro过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"></property>
<!-- 配置shiro过滤器pattern -->
<property name="filterChainDefinitions">
<value>
/static/** = anon <!--不需要登录验证-->
/login.jsp = anon <!--不需要登录验证-->
/**=authc <!--除指定请求外,其它所有的请求都需要身份验证-->
</value>
</property>
</bean>
<!-- 配置shiro安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"></bean>
spring的核心配置文件中:
<import resource="classpath:application-shiro.xml"/>
4. 登录认证
1)表单发送请求
2 ) 在Application-shiro.xml中指定登录认证
(这里的/login是第1步中发送post请求的url)
<!-- 配置登录认证的路径 --> <property name="loginUrl" value="/login" />
(写到这里项目在执行请求的时候会判断是不是login请求,如果不是,再判断有没有认证过,如果没有认证,执行对应的login请求,跳转到login.jsp页面(因为在controller中写了跳转方法)。如果执行的是login请求,就会做认证,下面写认证方法)
5. 创建登录realm(新建一个realm的包,再创建一个XXXRealm的类(这里我创建的是EmployeeRealm),这个类要继承AuthorizingRealm,并且实现里面的方法)
在application-shiro.xml中自定义并且注入realm:
实现认证:
6. 创建过滤器(监听认证成功了还是失败了,创建一个包filter,并且创建类MyFormFilter继承FormAuthenticationFilter)
在application-shiro.xml中配置:
(写到这的时候,点击登录的时候,如果用户名密码正确的时候就会到过滤器中onLoginSuccess方法,用户名或密码错误就会调用onLoginFailure方法)
登录成功相应给浏览器:
登录失败方法:
login请求:
7. 退出功能
登录成功显示用户名:
退出登录(在shiro.xml中写注销路径)
(写到这,用户名密码正确的时候可以成功跳到主页并显示用户名,点击注销的时候返回到登录页面。登录完成)
8. 授权
(当我们在控制器方法写了@RequiresPermissions,shiro在访问时,就会判断有没有该权限,如果没有,就不会执行对应方法)
1)在application-shiro配置文件中添加shiro注解
<!--
配置为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>
2)在controller类的方法中添加@RequiresPermissioins
3)授权调用:
/*授权
web doGetAuthorizationInfo什么时候调用
1.发现访问路径对应的方法上面 有授权注解 就会调用doGetAuthorizationInfo
2.页面当中有授权标签,也会调用doGetAuthorizationInfo
* */
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("授权调用------------");
/*获取用户的身份信息*/
Employee employee = (Employee) principalCollection.getPrimaryPrincipal();
/*根据当前用户,查询角色和权限*/
List<String> roles = new ArrayList<>();
List<String> permissions = new ArrayList<>();
/*判断当前用户是不是管理员 如果是管理员 拥有所有的权限*/
if(employee.getAdmin()){
/*拥有所有的权限*/
permissions.add("*:*");
}else {
/*查询角色*/
roles = employeeService.getRolesById(employee.getId());
/*查询权限*/
permissions = employeeService.getPermissionbyId(employee.getId());//查询角色和权限的方法省略
}
/*给授权信息*/
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRoles(roles);
info.addStringPermissions(permissions);
return info;
}
(写到这调用某一接口时,如果该用户(我这里是管理员有所有权限)有此权限,就会执行相应的方法)
9. 没有权限处理
在controller中写一个方法处理shiro异常
/*异常处理方法*/
@ExceptionHandler(AuthorizationException.class)
public void handleShiroException(HandlerMethod method, HttpServletResponse response) throws Exception { /*method是发生异常的方法*/
/*跳转到一个界面 界面提示没有权限*/
/*判断 当前的请求是不是json请求 如果是 返回json给浏览器 让它自己来做跳转*/
/*获取方法上的注解*/
ResponseBody methodAnnotation = method.getMethodAnnotation(ResponseBody.class);
if(methodAnnotation != null){
//ajax
AjaxRes ajaxRes = new AjaxRes();
ajaxRes.setSuccess(false);
ajaxRes.setMsg("您没有权限操作呢");
/*把对象转成json格式字符串*/
String jsonString = new ObjectMapper().writeValueAsString(ajaxRes);
response.setCharacterEncoding("utf-8");
response.getWriter().print(jsonString);
}else{
response.sendRedirect("nopermission.jsp");
}
}
新建一个nopermission.jsp,告诉用户没有权限
10.权限按钮控制
在controller中的方法上添加对应的权限,例如:
在需要权限控制的地方添加对应的shiro标签(记得引入shiro标签库
<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro" %>
)
(写到这调用某一接口时,如果该用户没有此权限,就会告诉用户没有权限,没有权限的标签不会显示出来)
11.散列密码加密
1)在保存用户的时候把密码进行加密
2)在认证中添加密码处理
3)添加凭证匹配器
12.授权缓存添加
(进入页面时, 页面当中写了Shiro的标签,每一个标签都要去到授权当中进行一次授权查询。授权查询只使用一次即可, 所以使用缓存,把对应的内容缓存起来,下次再去, 直接从缓存当中进行查询)
1)添加缓存pom依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.2</version>
</dependency>
2)添加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>
配置约束
3)application-shiro中配置缓存管理器
<!-- 缓存管理器 -->
<bean id="ehCache" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
</bean>
4)把缓存管理器添加到安全管理器当中
<!-- 配置shiro安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="employeeRealm" />
<property name="cacheManager" ref="ehCache"/>
</bean>
附录:
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: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
">
<bean id="myFormFilter" class="com.lyd.web.filter.MyFormFilter"/>
<!-- 配置shiro过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!--
其他的请求 会判断有没有认证过
index
默认情况下 : 没有认证,会自动跳转到login.jsp
配置了loginUrl : 如果没有认证,就会执行对应的login请求
login:
loginUrl:如果发现请求是loginUrl的值 会去做认证
配置登录认证的路径
-->
<property name="loginUrl" value="/login" />
<!--配置表单监听的过滤器-->
<property name="filters">
<map>
<entry key="authc" value-ref="myFormFilter"/>
</map>
</property>
<property name="securityManager" ref="securityManager"></property>
<!-- 配置shiro过滤器pattern -->
<property name="filterChainDefinitions">
<value>
/static/** = anon <!--不需要登录验证-->
/login.jsp = anon <!--不需要登录验证-->
/logout = logout <!--取消认证-->
/**=authc <!--除指定请求外,其它所有的请求都需要身份验证-->
</value>
</property>
</bean>
<!--自定义realm-->
<bean id="employeeRealm" class="com.lyd.web.realm.EmployeeReam">
<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="2"></property>
</bean>
<!-- 缓存管理器 -->
<bean id="ehCache" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
</bean>
<!-- 配置shiro安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--注入realm-->
<property name="realm" ref="employeeRealm"/>
<!--注入缓存-->
<property name="cacheManager" ref="ehCache"/>
</bean>
<!--
配置为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>
</beans>