spring整合apache-shiro的简单使用
作者:互联网
这里不对shiro进行介绍,介绍什么的没有意义
初学初用,不求甚解,简单使用
一.导入jar包(引入坐标)
<!--shiro和spring整合--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version> </dependency> <!--shiro核心包--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.3.2</version> </dependency>
二.在web.xml中配置shiro的过滤器
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <!-- 监听器监听其他的spring配置文件 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring/applicationContext-*.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 解决post乱码 --> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 指定加载的配置文件 ,通过参数contextConfigLocation加载--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <!--配置shiro的filter--> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <!--将shiro交给spring管理,不写默认是由tomcat进行管理--> <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>web.xml
注意:web.xml文件中的加载顺序为: context-param(ServletContext) -> listener-> filter -> servlet
,并且shiro的过滤器需要使用spring中配置的关于shiro的bean,所以shiro的配置不能写在springmvc的配置文件中,需要写在spring的配置文件中
三.编写shiro的配置(可以写在spring配置文件中,也可以单独一个文件)
本案例中spring配置文件名称为:applicationContext-service.xml
shiro的配置文件名称为:applicationContext-shiro.xml
dao层配置从spring配置中分离出来:applicationContext-dao.xml
在web.xml文件中引用方式为:
三.applicationContext-shiro.xml文件(shiro配置文件)
<!--主要三个东西:shiroFilter,SaasRealm,SecrityManager-->
注意: shiroFilter:这个名字必须和web.xml文件中配置的过滤器的名称一致
SaasRealm:自定义数据源(领域),只要继承抽象类AuthorizingRealm,实现抽象方法即可,其中一个方法是关于登录认证的(doGetAuthenticationInfo),一个方法是权限封装的(doGetAuthorizationInfo).
将从数据库中获取到的数据进行封装(权限)或者和客户端传入的数据进行比对(登录),返回相应的封装数据和登录结果
<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" 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"> <!--由spring整合shiro文件--> <!--主要三个东西:shiroFilter,SaasRealm,SecrityManager--> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!--shiro核心类--> <property name="securityManager" ref="securityManager"/> <!--登录页面,如果没有登录,会跳转到登录界面,--> <property name="loginUrl" value="/login.jsp"/> <!--登录后,在访问没有经过授权的方法或界面时,直接跳转到这个界面--> <property name="unauthorizedUrl" value="/unauthorized.jsp"/> <!--配置过滤器链--> <property name="filterChainDefinitions"> <value> /login.jsp = anon /css/** = anon /img/** = anon /plugins/** = anon /make/** = anon /login.do = anon /company/list.do = perms["企业管理"] /system/log/list.do=perms["日志管理"] /system/module/list.do=perms["模块管理"] /company/list.do=perms["企业管理"] /=perms["SaaS管理"] /cargo=perms["货运管理"] /stat=perms["统计分析"] /baseinfo=perms["基础信息"] /sysadmin=perms["系统管理"] /cargo/contract/list.do=perms["购销合同"] /cargo/contract/print.do=perms["出货表"] /cargo/export/contractList.do=perms["合同管理"] /cargo/export/list.do=perms["出口报运"] /stat/toCharts.do=perms["生产厂家销售情况"] /stat/toCharts.do=perms["产品销售排行"] /stat/toCharts.do=perms["系统访问压力图"] /system/dept/list.do=perms["部门管理"] /system/user/list.do=perms["用户管理"] /system/role/list.do=perms["角色管理"] /cargo/packing/list.do=perms["装箱管理"] /cargo/shipping/list.do=perms["委托管理"] /cargo/invoice/list.do=perms["发票管理"] /cargo/finance/list.do=perms["财务管理"] /sysadmin/deptAction_toview=perms["查看部门"] /sysadmin/deptAction_tocreate=perms["新增部门"] /sysadmin/deptAction_toupdate=perms["修改部门"] /sysadmin/deptAction_delete=perms["删除部门"] /** = authc </value> </property> </bean> <!--注入自定义的SaasRealm--> <bean id="saamRealm" class="com.ahd.realm.SaasRealm"/> <!--配置shiro核心类--> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="saamRealm"/> </bean> <!--后来编写的--> <!-- 安全管理器 --> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean> <!-- 保证实现了Shiro内部lifecycle函数的bean执行 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> <!-- 生成代理,通过代理进行控制 --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"> <property name="proxyTargetClass" value="true"/> </bean> <aop:aspectj-autoproxy proxy-target-class="true"/> </beans>
三.1路径匹配权限太多,不好写,不想使用注解来搞乱代码,自己编写了一个小工具类,实现拼接
package com.ahd.util; import com.ahd.domain.system.Module; import com.ahd.service.system.ModuleService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.util.List; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath*:spring/applicationContext-*.xml") public class realmUtil { @Autowired private ModuleService ms; @Test public void test() { String authorization = "perms"; List<Module> all = ms.findAll(); StringBuffer sb = new StringBuffer(); for (Module module : all) { String curl = module.getCurl(); if(curl!=null){ if (!curl.contains("?")) { sb.append("/" + curl + "=" + authorization + "[\"" + module.getName() + "\"]\n"); }else{ String[] split = curl.split("\\?"); sb.append("/" + split[0] + "=" + authorization + "[\"" + module.getName() + "\"]\n"); } }else{ sb.append("/" + curl + "=" + authorization + "[\"" + module.getName() + "\"]\n"); } } System.out.println(sb.toString()); } }自己编写的小工具
三.2想法
shiro对于路径匹配权限
没有好的解决方案,只能一条一条编写,
想法:编写一个工具类,动态查询数据库,拼接字符串,在spring容器加载之前写入文件(在web.xml中配置监听器)
没有好的解决方案,只能一条一条编写,
想法:编写一个工具类,动态查询数据库,拼接字符串,在spring容器加载之前写入文件(在web.xml中配置监听器)
四.自定义Realm
public class SaasRealm extends AuthorizingRealm { @Autowired private UserService us; @Autowired private ModuleService ms; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //将用户所有权限存入对象中 //获取登录的用户对象 User user = (User) principalCollection.getPrimaryPrincipal(); //通过查询数据库,查找该用户所具有的的权限 List<Module> moduleByUser = ms.findModuleByUser(user); // Set<String> perms=new HashSet<>(); //创建SimpleAuthorizationInfo权限对象 SimpleAuthorizationInfo info=new SimpleAuthorizationInfo(); for (Module module : moduleByUser) { info.addStringPermission(module.getName()); } return info; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken token= (UsernamePasswordToken) authenticationToken; //获取页面传递过来的账号密码 String username_page = token.getUsername();//email String password_page = new String(token.getPassword()); //从数据库中查找该账号的信息 User user_db = us.findByEmail(username_page); String password_db = user_db.getPassword(); //进行信息比较,如果密码不匹配,返回null,调用者会抛出异常 if(!password_page.equals(password_db)){ return null; } return new SimpleAuthenticationInfo(user_db,user_db.getPassword(),getName()); } }
部分代码图片解释:
五.登录代码示例
@RequestMapping(value="login",name = "用户登录") public String login(String email,String password){ //登录分析 //1.首先判断用户邮箱和密码是否为空 if(StringUtils.isEmpty(email)||StringUtils.isEmpty(password)){ request.setAttribute("error","邮箱或密码不能为空"); return "forward:/login.jsp"; } password=new Md5Hash(password,email,1).toString(); UsernamePasswordToken token=new UsernamePasswordToken(email,password); Subject subject = SecurityUtils.getSubject(); try { subject.login(token); } catch (AuthenticationException e) { request.setAttribute("error","邮箱或密码有误"); return "forward:/login.jsp"; } //执行到这里,证明登录成功,所以继续封装数据 //通过shiro的Session域获取user对象 User user = (User) subject.getPrincipal(); List<Module> moduleList =ms.findModuleByUser(user);//存入session session.setAttribute("modules",moduleList); //将登录数据存入session域 session.setAttribute("loginUser",user); return "home/main"; }
登录之后经过以上配置对于权限不需要再进行其他操作
六.注解配置
在路径对应的方法上添加注解@RequiresPermissions (“权限名称”)
七.页面标签展示
1.引入标签库:
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
2.使用标签
<shiro:hasPermission name="删除部门"> <button type="button" class="btn btn-default" title="删除" onclick='deleteById()'><i class="fa fa-trash-o"></i> 删除</button> </shiro:hasPermission>
八.优化
使用缓存,避免一项功能查询多次
增加了jvm负担,建议使用redis缓存
九.自定义过滤器
说明:模仿shiro现有的10个过滤器 现在模仿的是perms过滤器
目的:用此配置/system/module/list.do = perms["模块管理","删除模块"]
但凡符合其中的一个权限就应该放行,但是默认的perms过滤器必须两个都有才能放行
第一步:定义一个过滤器,继承一个父类
重写方法
public class MyPermsFilter extends AuthorizationFilter { public MyPermsFilter() { } public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException { Subject subject = this.getSubject(request, response); String[] perms = (String[])((String[])mappedValue); // perms = ["部门管理","删除部门"] if (perms != null && perms.length > 0) { for (String perm : perms) { if (subject.isPermitted(perm)) { return true; } } return false; } return true; } }
第二步:交给spring容器
第三步:把过滤器交个shiro框架
第四步:使用自定义的过滤器
关于缓存的问题,使用redis比较好推荐网页:
SpringDataRedis的操作
https://www.jianshu.com/p/4a9c2fec1079
标签:xml,do,String,perms,spring,list,apache,shiro 来源: https://www.cnblogs.com/aihuadung/p/11154813.html