shiro 整合SSM#
一.将shiro整合到ssm中
1.在整合完ssm后,添加shiro相关jar包(shiro-all.jar)
2.在web.xml中添加shiro的配置
<!-- 配置shiroFfilter通过代理来配置,对象由spring容器来创建,但是交由servlet容器来管理 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<!--表示bean的生命周期由servlet来管理 -->
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<!-- 表示在spring容器中bean的id,如果不配置该属性,那么默认和该filter的name一致 -->
<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>
3.在src下添加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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd" default-autowire="byName">
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 配置securityManager -->
<property name="securityManager" ref="securityManager"></property>
<!-- 当访问需要认证的资源时,如果没有认证,那么将自动跳转到该url,登陆url 如果不配置该属性会到根路径下的login.jsp-->
<property name="loginUrl" value="/login"></property>
<!-- 配置认证成功后跳转到那个url上,通常不设置,如果不设置,那么默认认证成功后跳转上上一个url -->
<property name="successUrl" value="/index"></property>
<!-- 配置用户没有权限访问资源时跳转的页面 -->
<property name="unauthorizedUrl" value="/refuse"></property>
<!--
配置shiro的过滤连
logout默认退出后跳转到根目录下,可以重新指定
-->
<property name="filterChainDefinitions">
<value>
/login=anon
/login=authc
/logout=logout
/js/**=anon
/css/**=anon
/images/**=anon
/**=authc
</value>
</property>
</bean>
<!-- 配置securityManager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm"></property>
</bean>
<!-- 配置自定义realm -->
<bean id="userRealm" class="com.bjsxt.realm.UserRealm">
</bean>
</beans>
4.修改Controller中的login登陆方法
//登陆
@RequestMapping("/login")
public ModelAndView login(HttpServletRequest request){
ModelAndView mv=new ModelAndView("login");
String className=(String) request.getAttribute("shiroLoginFailure");
if(UnknownAccountException.class.getName().equals(className)){
//抛出自定义异常
mv.addObject("msg","用户名或密码错误");
}else if(IncorrectCredentialsException.class.getName().equals(className)){
mv.addObject("msg", "用户名或密码错误");
}else{
mv.addObject("msg", "系统异常");
}
return mv;
}
5.添加自定义Realm:UserRealm
public class UserRealm extends AuthorizingRealm{
@Override
public String getName() {
return "userRealm";
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String userName = token.getPrincipal().toString();
String pwd="1234";
return new SimpleAuthenticationInfo(userName,pwd,getName());
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
二.修改userRealm是实现身份认证
public class UserRealm extends AuthorizingRealm{
@Autowired
private UserService userService;
@Autowired
private Permissionservice permissionservice;
@Override
public String getName() {
return "userRealm";
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String userName = token.getPrincipal().toString();
User user=userservice.findUserByName(username);
if(user!=null){
user.setMenus(permissionService.findByUserId(user.getId()));
}
return new SimpleAuthenticationInfo(userName,pwd,getName());
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
三.配置授权
1.在自定义UserRealm中的doGetAuthorizationInfo()方法中
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//获取身份验证信息 该身份验证信息在认证时已设置
Users users=(Users)principals.getPrimaryPrincipal();
//根据userId得到其权限
List<Permission> permissions=permissionService.findPermissionByUserId(users.getId());
if(permissions==null||permissions.size()){
return null;
}
SimpleAuthenticationInfo info=new SimpleAuthenticationInfo();
for(Permission p:permissions){
info.addStringPermission(p.getPercode);
}
return info;
}
2.想让授权起作用还需要配置两个地方,一个是注解起作用,一个是页面起作用。和在springmvc配置文件中添加aop代理,并且添加异常处理(注解起对权限作用)
<!-- 在springmvc中开启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.SimpleMappingExceptionResolve">
<property name="exceptionMappings">
<props>
<!-- key是异常完全限定名 值是视图名 -->
<!-- 认证异常 -->
<prop key="org.apache.shiro.authz.UnauthenticatedException">login</prop>
<!-- 授权异常 -->
<prop key="org.apache.shiro.authz.UnauthorizedException">refuse</prop>
</props>
</property>
</bean>
3.在jsp页面中,添加按钮权限检查(页面对权限起作用)
-
首先需要导入shiro的标准库
<%@ taglib prefix=“shiro” uri=“http://shiro.apache.org/tags”%>
-
在需要检查权限的地方使用shiro标签
<!–当前用户有update权限,显示修改操作->
<shiro:hasPermission name=“role:update”>修改</shiro:hasPermission>
4.在controller方法中添加权限检查注解
@RequestMapping("/list")
@RequiresPermissions("Role:list")
ModelANdView mv=new ModelAndViewe("role/list"){
mv.addObject("list",roleService.findAll());
return mv;
}
四.缓存
1.不能将授权功能放在认证功能中,因为有可能管理员在用户登陆操作时修改用户权限,如果将授权功能放在认证功能中,则只要用户不登出,管理员修改的权限不生效,用户还可操作。
2.但不把授权放在认证中,每次都会去数据库查询权限信息,此时可用缓存处理。写个缓存方法,当管理员修改权限后清理缓存方法
3.每次权限检查都会导数据库中获取权限,这样效率低。可以通过设置缓存来解决该问题。Shiro可以和ehcache或redis集成。
4.步骤
-
将eacache.jar导入系统
-
shiro默认集成了一个ehcache.xml的配置文件。也可以自己添加一个进行配置,如果自定义配置文件,放在src下。
<ehcache> <diskStore path="java.io.tmpdir/shiro-ehcache"/> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" /> </ehcache>
-
在applicationContext-shiro.xml中,添加cacheManager的配置
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="userRealm"></property> <property name="cachemManager" ref="cacheManager"></property> </bean> <!-- 配置缓存管理器 --> <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"></property> </bean>
-
如果在运行过程中,主体的权限发生了改变,那么应该从spring容器中调用realm中的清理缓存方法,进行清理
//清理缓存方法 /*@Override protected void clearCache(PrincipalCollection principals) { super.clearCache(principals); }*/ protected void clearCache() { Subject subject = SecurityUtils.getSubject(); super.clearCache(subject.getPrincipals()); }
五.会话(session)管理
1.配置securityManager
<!-- 配置securityManager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm"></property>
<property name="cachemManager" ref="cacheManager"></property>
<property name="sessionManager" ref="sessionManager"></property>
</bean>
2.配置会话管理器
<!-- 配置会话管理器 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<!-- 单位是毫秒 -->
<property name="globalSessionTimeout" value="300000"></property>
<!-- 删除无效session -->
<property name="deleteInvalidSessions" value="true"></property>
</bean>
六.记住我
1.将用户类实现序列化接口,该类的引用类也必须实现序列化接口
2.设置登陆时表单中“记住我”的域名
<!-- 配置authc过滤器 -->
<bean id="authc" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
<property name="usernameParam" value="name"></property>
<property name="passwordParam" value="pwd"></property>
<property name="rememberMeParam" value="rememberMe"></property>
</bean>
3.设置“记住我”管理器
<!-- 配置securityManager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm"></property>
<property name="cachemManager" ref="cacheManager"></property>
<property name="sessionManager" ref="sessionManager"></property>
<property name="rememberMeManager" ref="rememberMeManager"></property>
</bean>
<!-- 记住我配置 -->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<property name="cookie" ref="rememberMeCookie"></property>
</bean>
<!-- 记住我cookie -->
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<!-- 设置cookie的存活时间 -->
<property name="maxAge" value="604800"></property>
<!-- 设置cookie名称 -->
<property name="name" value="rememberMe"></property>
</bean>
4.在过滤链中配置哪些资源通过记住我可以再次访问
<property name="filterChainDefinitions">
<value>
/login=anon
/login=authc
/logout=logout
/js/**=anon
/css/**=anon
/index=user
/images/**=anon
/**=authc
</value>
</property>
5.页面设置
<input type="checkbox" name="rememberMe">