1、整合ssm并且实现用户登录和菜单权限
2、将shiro整合到ssm中
(1)添加shiro相关jar包
(2)在web.xml中添加shiro配置
1 <!-- 新增shiro配置 --> 2 <!-- 配置shiroFilter,通过代理来配置,对象由spring容器来创建的,但是交由servlet容器来管理 --> 3 <filter> 4 <filter-name>shiroFilter</filter-name> 5 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 6 <init-param> 7 <!-- 表示bean的生命周期由servlet管理 --> 8 <param-name>targetFilterLifecycle</param-name> 9 <param-value>true</param-value> 10 </init-param> 11 12 <init-param> 13 <!-- 表示在spring容器中bean的id,如果不配置该属性,那么默认和filter的name一致 --> 14 <param-name>targetBeanName</param-name> 15 <param-value>shiroFilter</param-value> 16 </init-param> 17 18 </filter> 19 20 <filter-mapping> 21 <filter-name>shiroFilter</filter-name> 22 <url-pattern>/*</url-pattern> 23 </filter-mapping> 24 25 <!-- shiro结束 -->
(3)添加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-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd"> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 配置securityManager --> <property name="securityManager" ref="securityManager"/> <!-- 当访问需要认证的资源时,如果没有认证,那么将自动跳转到该url; 如果不配置该属性,默认情况下货到根路径下的login.jsp --> <!-- controller层查看地址配置 --> <property name="loginUrl" value="/login"></property> <!-- 配置认证成功后,跳转到那个url上,通常不设置,如果不设置,那么默认认证成功后跳转到上一个url --> <property name="successUrl" value="/index"></property> <!-- 配置用户没有权限访问资源时,跳转的页面 /refuse自定义 --> <property name="unauthorizedUrl" value="/refuse"></property> <!-- 配置shiro的过滤器链 --> <property name="filterChainDefinitions"> <value> <!-- anon表示匿名 --> /toLogin=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"/> </bean> <bean id="userRealm" class="com.sun123.template.realm.UserRealm"/> </beans>
(4)修改LoginController中的登录方法
package com.sun123.template.conrtroller; import javax.servlet.http.HttpServletRequest; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; @Controller public class LoginController { @RequestMapping(value = {"/","index"}) public ModelAndView index() { return new ModelAndView("index"); } //去登录页面 @RequestMapping("/toLogin") public ModelAndView toLogin() { return new ModelAndView("login"); } //登录 @RequestMapping("/login") public ModelAndView login(HttpServletRequest request) { System.out.println("========login======="); 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; } //访问被拒绝 @RequestMapping("/refuse") public ModelAndView refuse() { return new ModelAndView("refuse"); } }
(5)添加自定义Realm:UserRealm.java
package com.sun123.template.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; public class UserRealm extends AuthorizingRealm { @Override public String getName() { // TODO Auto-generated method stub return "userRealm"; } //获取认证信息 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("---------认证----------------"); String username = token.getPrincipal().toString(); String pwd = "1111"; return new SimpleAuthenticationInfo(username,pwd,getName()); } //获取授权信息 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) { return null; } }
3、修改UserRealm实现自定义认证
1 package com.sun123.template.realm; 2 3 import org.apache.shiro.authc.AuthenticationException; 4 import org.apache.shiro.authc.AuthenticationInfo; 5 import org.apache.shiro.authc.AuthenticationToken; 6 import org.apache.shiro.authc.SimpleAuthenticationInfo; 7 import org.apache.shiro.authz.AuthorizationInfo; 8 import org.apache.shiro.realm.AuthorizingRealm; 9 import org.apache.shiro.subject.PrincipalCollection; 10 import org.springframework.beans.factory.annotation.Autowired; 11 12 import com.sun123.template.entity.User; 13 import com.sun123.template.service.UserService; 14 15 public class UserRealm extends AuthorizingRealm { 16 17 @Autowired 18 private UserService userService; 19 20 @Override 21 public String getName() { 22 // TODO Auto-generated method stub 23 return "userRealm"; 24 } 25 26 //获取认证信息 27 @Override 28 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 29 System.out.println("---------认证----------------"); 30 String username = token.getPrincipal().toString(); 31 User user = userService.findByUserName(username); 32 return new SimpleAuthenticationInfo(user,user.getPassword(),getName()); 33 } 34 35 //获取授权信息 36 @Override 37 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) { 38 39 return null; 40 } 41 42 43 44 }
4、凭证匹配器配置
1 <!-- 配置自定义realm --> 2 <bean id="userRealm" class="com.sun123.template.realm.UserRealm"> 3 <property name="credentialsMatcher" ref="credentialsMatcher" /> 4 </bean> 5 <!-- 配置凭证匹配器 --> 6 <bean id="credentialsMatcher" 7 class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> 8 <property name="hashAlgorithmName" value="md5" /> 9 <!-- 迭代次数 --> 10 <property name="hashIterations" value="2" /> 11 </bean>
UserRealm.java要相应改变
1 package com.sun123.template.realm; 2 3 import org.apache.shiro.authc.AuthenticationException; 4 import org.apache.shiro.authc.AuthenticationInfo; 5 import org.apache.shiro.authc.AuthenticationToken; 6 import org.apache.shiro.authc.SimpleAuthenticationInfo; 7 import org.apache.shiro.authz.AuthorizationInfo; 8 import org.apache.shiro.realm.AuthorizingRealm; 9 import org.apache.shiro.subject.PrincipalCollection; 10 import org.apache.shiro.util.ByteSource; 11 import org.springframework.beans.factory.annotation.Autowired; 12 13 import com.sun123.template.entity.User; 14 import com.sun123.template.service.UserService; 15 16 public class UserRealm extends AuthorizingRealm { 17 18 @Autowired 19 private UserService userService; 20 21 @Override 22 public String getName() { 23 // TODO Auto-generated method stub 24 return "userRealm"; 25 } 26 27 //获取认证信息 28 @Override 29 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 30 System.out.println("---------认证----------------"); 31 String username = token.getPrincipal().toString(); 32 User user = userService.findByUserName(username); 33 return new SimpleAuthenticationInfo(user,user.getPassword(),ByteSource.Util.bytes(user.getPasswordSalt()),getName()); 34 } 35 36 //获取授权信息 37 @Override 38 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) { 39 40 return null; 41 } 42 43 44 45 }
5、logout配置,默认退出后跳转到根路径下,如果需要改变则需重新配置logout过滤器,过滤器的id不能改变,只能为logout
1 <bean id="shiroFilter" 2 class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> 3 <!-- 配置securityManager --> 4 <property name="securityManager" ref="securityManager" /> 5 <!-- 当访问需要认证的资源时,如果没有认证,那么将自动跳转到该url; 如果不配置该属性,默认情况下货到根路径下的login.jsp --> 6 <!-- controller层查看地址配置 --> 7 <property name="loginUrl" value="/login"></property> 8 <!-- 配置认证成功后,跳转到那个url上,通常不设置,如果不设置,那么默认认证成功后跳转到上一个url --> 9 <property name="successUrl" value="/index"></property> 10 <!-- 配置用户没有权限访问资源时,跳转的页面 /refuse自定义 --> 11 <property name="unauthorizedUrl" value="/refuse"></property> 12 <!-- 配置shiro的过滤器链 13 logout默认退出后跳转到根路径下,可以重新指定 14 --> 15 <property name="filterChainDefinitions"> 16 <value> 17 <!-- anon表示匿名 --> 18 /toLogin=anon 19 /login=authc 20 /logout=logout 21 /js/**=anon 22 /css/**=anon 23 /images/**=anon 24 /**=authc 25 </value> 26 </property> 27 </bean> 28 29 <!-- 配置authc过滤器(用户名和密码不使用默认配置username,password) --> 30 <bean id="authc" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter"> 31 <property name="usernameParam" value="name"/> 32 <property name="passwordParam" value="pwd"/> 33 </bean> 34 35 <!-- 配置logout过滤器 --> 36 <bean id="logout" class="org.apache.shiro.web.filter.authc.LogoutFilter"> 37 <property name="redirectUrl" value="/toLogin"/> 38 </bean>
6、改变登录时的表单域名称,需要重新配置authc过滤器
1 <bean id="shiroFilter" 2 class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> 3 <!-- 配置securityManager --> 4 <property name="securityManager" ref="securityManager" /> 5 <!-- 当访问需要认证的资源时,如果没有认证,那么将自动跳转到该url; 如果不配置该属性,默认情况下货到根路径下的login.jsp --> 6 <!-- controller层查看地址配置 --> 7 <property name="loginUrl" value="/login"></property> 8 <!-- 配置认证成功后,跳转到那个url上,通常不设置,如果不设置,那么默认认证成功后跳转到上一个url --> 9 <property name="successUrl" value="/index"></property> 10 <!-- 配置用户没有权限访问资源时,跳转的页面 /refuse自定义 --> 11 <property name="unauthorizedUrl" value="/refuse"></property> 12 <!-- 配置shiro的过滤器链 13 logout默认退出后跳转到根路径下,可以重新指定 14 --> 15 <property name="filterChainDefinitions"> 16 <value> 17 <!-- anon表示匿名 --> 18 /toLogin=anon 19 /login=authc 20 /logout=logout 21 /js/**=anon 22 /css/**=anon 23 /images/**=anon 24 /**=authc 25 </value> 26 </property> 27 </bean> 28 29 <!-- 配置authc过滤器(用户名和密码不使用默认配置username,password) --> 30 <bean id="authc" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter"> 31 <property name="usernameParam" value="name"/> 32 <property name="passwordParam" value="pwd"/> 33 </bean>
登录页面的改变:
1 <form action="login" method="post"> 2 <!-- username:<input type="text" name="username"><br/> 3 password:<input type="password" name="password"><br/> --> 4 username:<input type="text" name="name"><br/> 5 password:<input type="password" name="pwd"><br/> 6 <input type="submit" value="login"> 7 </form>
7、授权
(1)修改自定义Realm进行权限检查
(2)在spring-mvc的配置文件中,添加AOP代理,并且添加异常处理
1 <!-- 开启AOP代理 --> 2 <aop:config proxy-target-class="true"></aop:config> 3 <!-- 开启Shiro注解支持 --> 4 <bean 5 class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> 6 <property name="securityManager" ref="securityManager" /> 7 </bean>
在controller的处理方法中,添加权限检测的注解
在jsp页面中,添加按钮权限检测,首先需要导入shiro的标签库
在需要检查权限的地方,使用shiro标签
8、缓存
每次检查权限都会到数据库中获取权限,这样效率很低。可以通过设置缓存来解决该问题。Shiro可以和ehcache或者redis集成。这里使用ehcache来缓存数据。
(1)将ehcache,jar导入系统。
(2)shiro默认集成了一个ehcache的配置文件。也可以自己添加一个进行配置,放入resources下。
ehcache.xml:
1 <ehcache> 2 <diskStore path="java.io.tmpdir/shiro-ehcache"/> 3 <defaultCache 4 maxElementsInMemory="10000" 5 eternal="false" 6 timeToIdleSeconds="120" 7 timeToLiveSeconds="120" 8 overflowToDisk="false" 9 diskPersistent="false" 10 diskExpiryThreadIntervalSeconds="120" 11 /> 12 </ehcache>
(3)在applicationContext-shiro.xml中,添加cacheManager的配置
1 <!-- 配置securityManager --> 2 <bean id="securityManager" 3 class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> 4 <property name="realm" ref="userRealm" /> 5 <property name="cacheManager" ref="cacheManager"></property> 6 </bean> 7 <!-- 配置缓存管理器 --> 8 <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> 9 <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"></property> 10 </bean>
(4)如果在运行过程中,主体的权限发生了改变,那么应该从spring容器中调用realm中的清理缓存方法,进行清理。
UserRealm.java中添加清理缓存方法
1 //清理缓存方法 2 protected void clearCache() { 3 Subject subject = SecurityUtils.getSubject(); 4 super.clearCache(subject.getPrincipals()); 5 }
9、会话管理
1 <!-- 配置securityManager --> 2 <bean id="securityManager" 3 class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> 4 <property name="realm" ref="userRealm" /> 5 <property name="cacheManager" ref="cacheManager"/> 6 <property name="sessionManager" ref="sessionManager"/> 7 </bean> 8 <!-- 配置缓存管理器 --> 9 <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> 10 <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"></property> 11 </bean> 12 13 <!-- 配置会话管理器 --> 14 <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> 15 <!-- 失效时间单位是毫秒 --> 16 <property name="globalSessionTimeout" value="300000"/> 17 <!-- 删除无效session --> 18 <property name="deleteInvalidSessions" value="true"/> 19 </bean>
10、记住我
(1)将用户类实现序列化接口,该类的引用类也必须实现序列化接口
(2)设置登录时表单中“记住我”的域名
1 <!-- 配置authc过滤器(用户名和密码不使用默认配置username,password) --> 2 <bean id="authc" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter"> 3 <property name="usernameParam" value="name"/> 4 <property name="passwordParam" value="pwd"/> 5 <property name="rememberMeParam" value="rememberMe"/> 6 </bean>
(3)设置“记住我”管理器
1 <!-- 配置securityManager --> 2 <bean id="securityManager" 3 class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> 4 <property name="realm" ref="userRealm" /> 5 <property name="cacheManager" ref="cacheManager"/> 6 <property name="sessionManager" ref="sessionManager"/> 7 <property name="rememberMeManager" ref="rememberMeManager"/> 8 </bean> 9 <!-- 配置缓存管理器 --> 10 <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> 11 <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"></property> 12 </bean> 13 14 <!-- 配置会话管理器 --> 15 <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> 16 <!-- 失效时间单位是毫秒 --> 17 <property name="globalSessionTimeout" value="300000"/> 18 <!-- 删除无效session --> 19 <property name="deleteInvalidSessions" value="true"/> 20 </bean> 21 <!-- 记住我配置 --> 22 <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager"> 23 <property name="cookie" ref="rememberMeCookie"></property> 24 </bean> 25 <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> 26 <!-- 设置cookie存活时间 保存7天 --> 27 <property name="maxAge" value="604800"/> 28 <!-- 设置cookie的名称 --> 29 <property name="name" value="rememberMe"/> 30 </bean> 31 <!-- 记住我配置结束 -->
(4)在过滤器链中配置哪些资源通过记住我就可以再次访问
1 <!-- 配置shiro的过滤器链 2 logout默认退出后跳转到根路径下,可以重新指定 3 --> 4 <property name="filterChainDefinitions"> 5 <value> 6 <!-- anon表示匿名 --> 7 /toLogin=anon 8 /login=authc 9 /logout=logout 10 /js/**=anon 11 /css/**=anon 12 /images/**=anon 13 /easyui/**=anon 14 /index=user 15 /**=authc 16 </value> 17 </property>
(5)jsp页面设置
1 <form action="login" method="post"> 2 <!-- username:<input type="text" name="username"><br/> 3 password:<input type="password" name="password"><br/> --> 4 username:<input type="text" name="name"><br/> 5 password:<input type="password" name="pwd"><br/> 6 <input type="checkbox" name="rememberMe">记住我<br/> 7 <input type="submit" value="login"> 8 </form>