shiro基本配置和使用
这里讲shiro整合spring 实现登录验证 和权限拦截
首先pom文件添加依赖
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.3.2</version>
</dependency>
<!-- spring整合shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- ehcache-->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.11</version>
</dependency>
shiro要结合ehcache这个缓存框架 可以实现一些功能 比如限制用户登录密码错误次数 然后限制多久时间内,限制登录
接下来就来就是配置文件 首先web.xml
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>DelegatingFilterProxy</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
这是配置一个shiro过滤器 将所有的请求都交给shiro处理 至于spring的配置我这里就不写了 和正常的配置一样
然后ehcache缓存框架需要一个配置文件 我这里贴出来 文件名就叫 ehcache.xml
1 <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"> 2 3 <diskStore path="${webapp.root}"/> 4 5 <defaultCache 6 maxElementsInMemory="10000" 7 maxElementsOnDisk="0" 8 eternal="true" 9 overflowToDisk="true" 10 diskPersistent="false" 11 timeToIdleSeconds="0" 12 timeToLiveSeconds="0" 13 diskSpoolBufferSizeMB="50" 14 diskExpiryThreadIntervalSeconds="120" 15 memoryStoreEvictionPolicy="LFU" 16 /> 17 </ehcache>
接下来就是spring整合shiro的配置文件
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 5 6 7 <!-- 配置ehCache缓存支持--> 8 <bean name="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> 9 <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"></property> 10 </bean> 11 12 13 <!-- 自定义Realm--> 14 <bean name="userRealm" class="com.newer.web.shiro.MyRealm" > 15 16 <!--注入加密算法类 --> 17 <property name="credentialsMatcher"> 18 19 <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> 20 <!--加密算法名 --> 21 <property name="hashAlgorithmName" value="MD5"/> 22 <!--加密次数 --> 23 <property name="hashIterations" value="6"/> 24 </bean> 25 26 </property> 27 28 29 </bean> 30 31 32 <!-- shiro安全管理器--> 33 <bean name="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> 34 <property name="cacheManager" ref="cacheManager"></property> 35 <property name="realm" ref="userRealm"></property> 36 </bean> 37 38 39 <!-- 管理shiro bean的生命周期 --> 40 <bean name="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean> 41 42 47 48 <!--shiro核心过滤器配置--> 49 <bean name="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> 50 <property name="securityManager" ref="securityManager"/> 51 <property name="loginUrl" value="/login.jsp"></property> <!-- 没有认证 跳转的登录连接 --> 52 <property name="unauthorizedUrl" value="/unauthorized.jsp"></property> <!-- 没有访问权限 跳转的页面 -->
82 <!--这里使用 从数据库查询出来要拦截的url集合 注入到这个对象中 实现拦截 --> 83 <property name="filterChainDefinitionMap" ref="map"/> 86 </bean> 87 88 89 <!--实例工厂方法 将执行shiroFilterUtils类的build的方法 --> 90 <bean name="map" factory-bean="shiroFilterUtils" factory-method="build"/> 91 92 <!--这个类将从数据库中查询要拦截的url信息--> 93 <bean name="shiroFilterUtils" class="com.newer.util.ShiroFilterUtils"/> 97 </beans>
别的配置不说 shiro核心过滤器bean的 name的值 一定要与web.xml里面配置的shiro过滤器的名字一样 否则会报错
然后我这里是使用实例工厂方法 将一个存有要拦截的url的map集合 注入到ShiroFilterFactoryBean 中的filterChainDefinitionMap属性
这样当我们的请求经过shiro过滤器时 会根据我们配置的要拦截的url和使用相应的拦截器 对请求进行处理
现在把我这个工具类贴出来
/** * 这个类会在 spring容器初始化bean时 从数据库将要拦截的url * 查询出来 封装成集合 注入到ShiroFilterFactoryBean类的filterChainDefinitions属性中 * 这样实现对需要相应权限才能访问的url进行拦截 并由shiro验证是否具有相应权限 */ public class ShiroFilterUtils { public Map<String,String> build(){ Map<String,String> map= new LinkedHashMap<String, String>();
//添加固定要拦截的url map.put("/user/login.do","anon"); map.put("/login.jsp","anon"); map.put("/**","authc"); return map; } }
这里讲一下 url和拦截器的语法
url使用的是 Ant风格
? 匹配任何单字符
* 匹配0或者任意数量的字符
** 匹配0或者更多的目录
拦截器常用有:
anon 匿名的 不需要认证 也可以访问
authc 必须认证才可以访问 没有认证会跳转登录链接
user 登录之后启用了记住我功能 就可以访问
roles 例如 roles[管理员] 表示必须具有管理员权限才可操作 如果是roles[管理员,经理] 表示要具有管理员和经理权限才可以访问
------------------------------------------
shiro会将请求的url按照顺序和map集合中的所有url匹配 如果匹配到了 不会再匹配后续url 所以要注意 map集合中要拦截的url的顺序
我这里就没有将要拦截的url 从数据库中查出 如果需要从数据集中查出 然后将要拦截的url作为key 要使用的拦截器作为value 存入这个map集合即可
权限管理就这么多 接下来说登录认证 贴出我的controller
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/login") public String login(String userName, String userPassword, ModelMap map){ //得到当前 Subject Subject currentSubject= SecurityUtils.getSubject(); //创建用户名密码 token UsernamePasswordToken token= new UsernamePasswordToken(userName,userPassword); //记住当前用户 关掉浏览器 重开也能访问 不是authc 拦截器的url token.setRememberMe(true); try { //执行登录方法 currentSubject.login(token); }catch (IncorrectCredentialsException ie){ //将抛出的登录异常 存储 然后返回登录页面 显示给用户 map.put("loginMessage","用户名或密码错误!!!"); return "forward:/login.jsp"; }catch (AuthenticationException ae){ //所有异常的父类 map.put("loginMessage","未知错误 请联系管理员"); return "forward:/login.jsp"; } return "success"; } @RequestMapping("/logout") public String logout(){ //得到当前 Subject Subject currentSubject= SecurityUtils.getSubject(); //注销当前 Subject currentSubject.logout(); return "redirect:/login.jsp"; } //这里面没有去写注册方法了 注册的时候 要将密码 通过这样加密后存入到数据库 public static void main(String[] args) { //生成MD5加密的密码 参1:加密方式 ,参2:未加密的密码 参3:盐值 使用当前用户名 参4:加密次数 Object str= new SimpleHash("MD5","123456","zhangsan",6); System.out.println("str = " + str); } }
当执行login()方法时 会调用我们在shiro配置文件中配置的自定义Realm类 login方法出现错误 会抛出异常 我们用catch可以捕获对应异常
接下来贴出我的Realm类
@Component public class MyRealm extends AuthorizingRealm { @Autowired private UserService userService; @Autowired private AuthorityService authorityService; @Override //认证方法 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //将token强转成 用户密码token UsernamePasswordToken token=(UsernamePasswordToken)authenticationToken; //根据传入的用户名 查询数据库 返回用户对象 User user=userService.queryByUserName(token.getUsername()); //没有查到用户 直接抛出异常 if(user==null){ throw new IncorrectCredentialsException(); } //根据当前用户名 生成盐值 用于结合加密算法对密码进行加密 ByteSource salt=ByteSource.Util.bytes(token.getUsername()); //将用户名和密码 交由shiro认证 如果出错会抛出对应异常 参数1:用户名 参数2:用户密码 参3:盐值 参数4:当前Realm的名字 return new SimpleAuthenticationInfo(user.getUserName(),user.getUserPassword(),salt,this.getName()); } @Override //授权方法 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //获得用户名 String userName= (String) principalCollection.getPrimaryPrincipal(); //根据用户名 查询当前用户 拥有的权限 Set<String> roles=authorityService.queryByUserName(userName); //将当前权限集合 返回交给shiro处理 权限认证 return new SimpleAuthorizationInfo(roles); } }
第二个授权方法 就是当请求需要对应权限的拦截器的url时 shiro会调用这个方法 然后我们根据当前用户名 查询出当前用户所拥有的权限
然后交给shiro 由他去比对当前用户是否拥有可以访问的资格 如果没有就会跳转到我们在配置文件中 shiro过滤器配置的unauthorizedUrl属性的值去
shiro的基本用法就这些 网上看了很多教程 都是各不相同 不像别的框架 教程都差不多 暂时总结到这里 后续有新的用法 再来加上吧