1.取消原springmvc认证和授权拦截器
去掉springmvc.xml中配置的LoginInterceptor和PermissionInterceptor拦截器。
2.web.xml添加shiro Filter
<!-- shiro过虑器,DelegatingFilterProxy通过代理模式将spring容器中的bean和filter关联起来 --> <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>
3.applicationContext-shiro.xml
<!-- Shiro 的Web过滤器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 --> <property name="loginUrl" value="/login.action" /> <property name="unauthorizedUrl" value="/refuse.jsp" /> <!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 --> <property name="filterChainDefinitions"> <value> <!-- 退出拦截,请求logout.action执行退出操作 --> /logout.action = logout <!-- 无权访问页面 --> /refuse.jsp = anon <!-- roles[XX]表示有XX角色才可访问 --> /item/list.action = roles[item],authc /js/** anon /images/** anon /styles/** anon /validatecode.jsp anon /item/* authc <!-- user表示身份认证通过或通过记住我认证通过的可以访问 --> /** = authc </value> </property> </bean> <!-- 安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="userRealm" /> </bean> <!-- 自定义 realm --> <bean id="userRealm" class="cn.itcast.ssm.realm.CustomRealm1"> </bean>
securityManager:这个属性是必须的。
loginUrl:没有登录认证的用户请求将跳转到此地址进行认证,不是必须的属性,不输入地址的话会自动寻找项目web项目的根目录下的”/login.jsp”页面。
unauthorizedUrl:没有权限默认跳转的页面。
4.自定义realm
此realm先不从数据库查询权限数据,当前需要先将shiro整合完成,在上边章节定义的realm基础上修改。
public class CustomRealm1 extends AuthorizingRealm { @Override public String getName() { return "customRealm"; } // 支持什么类型的token @Override public boolean supports(AuthenticationToken token) { return token instanceof UsernamePasswordToken; } // 认证 @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { // 从token中 获取用户身份信息 String username = (String) token.getPrincipal(); // 拿username从数据库中查询 // .... // 如果查询不到则返回null if (!username.equals("zhang")) {// 这里模拟查询不到 return null; } // 获取从数据库查询出来的用户密码 String password = "123";// 这里使用静态数据模拟。。 // 根据用户id从数据库取出菜单 //...先用静态数据 List<SysPermission> menus = new ArrayList<SysPermission>();; SysPermission sysPermission_1 = new SysPermission(); sysPermission_1.setName("商品管理"); sysPermission_1.setUrl("/item/queryItem.action"); SysPermission sysPermission_2 = new SysPermission(); sysPermission_2.setName("用户管理"); sysPermission_2.setUrl("/user/query.action"); menus.add(sysPermission_1); menus.add(sysPermission_2); // 构建用户身份信息 ActiveUser activeUser = new ActiveUser(); activeUser.setUserid(username); activeUser.setUsername(username); activeUser.setUsercode(username); activeUser.setMenus(menus); // 返回认证信息由父类AuthenticatingRealm进行认证 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo( activeUser, password, getName()); return simpleAuthenticationInfo; } // 授权 @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { // 获取身份信息 ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal(); //用户id String userid = activeUser.getUserid(); // 根据用户id从数据库中查询权限数据 // ....这里使用静态数据模拟 List<String> permissions = new ArrayList<String>(); permissions.add("item:query"); permissions.add("item:update"); // 将权限信息封闭为AuthorizationInfo SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); for (String permission : permissions) { simpleAuthorizationInfo.addStringPermission(permission); } return simpleAuthorizationInfo; } }
5.登录
// 用户登陆提交 @RequestMapping("/login") public String loginsubmit(Model model, HttpServletRequest request) throws Exception { // shiro在认证过程中出现错误后将异常类路径通过request返回 String exceptionClassName = (String) request .getAttribute("shiroLoginFailure"); if(exceptionClassName!=null){ if (UnknownAccountException.class.getName().equals(exceptionClassName)) { throw new CustomException("账号不存在"); } else if (IncorrectCredentialsException.class.getName().equals( exceptionClassName)) { throw new CustomException("用户名/密码错误"); } else if("randomCodeError".equals(exceptionClassName)){ throw new CustomException("验证码错误"); } else{ throw new Exception();//最终在异常处理器生成未知错误 } } return "login"; }
6.首页
由于session由shiro管理,需要修改首页的controller方法,将session中的数据通过model传到页面。
//系统首页 @RequestMapping("/first") public String first(Model model)throws Exception{ //主体 Subject subject = SecurityUtils.getSubject(); //身份 ActiveUser activeUser = (ActiveUser) subject.getPrincipal(); model.addAttribute("activeUser", activeUser); return "/first"; }
7.退出
由于使用shiro的sessionManager,不用开发退出功能,使用shiro的logout拦截器即可。
<!-- 退出拦截,请求logout.action执行退出操作 --> /logout.action = logout
8.无权限refuse.jsp
当用户无操作权限,shiro将跳转到refuse.jsp页面。