【2】过滤器链
定义:authentication.properties
#静态资源不过滤
/static/**=anon
#登录链接不过滤
/login/**=anon
#其他链接是需要登录的
/**=authc
注意:这里定义的过滤器是有执行顺序的,从上向下执行
【3】加载原理分析
定义:PropertiesUtil,从classpath中加载authentication.properties
1. package com.itheima.shiro.properties; 2. 3. import com.itheima.shiro.utils.EmptyUtil; 4. import lombok.extern.log4j.Log4j2; 5. 6. /** 7. * @Description 读取Properties的工具类 8. */ 9. @Log4j2 10. public class PropertiesUtil { 11. 12. public static LinkProperties propertiesShiro = new LinkProperties(); 13. 14. /** 15. * 读取properties配置文件信息 16. */ 17. static { 18. String sysName = System.getProperty("sys.name"); 19. if (EmptyUtil.isNullOrEmpty(sysName)) { 20. sysName = "application.properties"; 21. } else { 22. sysName += ".properties"; 23. } 24. try { 25. propertiesShiro.load(PropertiesUtil.class.getClassLoader() 26. .getResourceAsStream("authentication.properties")); 27. } catch (Exception e) { 28. log.warn("资源路径中不存在authentication.properties权限文件,忽略读取!"); 29. } 30. } 31. 32. /** 33. * 根据key得到value的值 34. */ 35. public static String getShiroValue(String key) { 36. return propertiesShiro.getProperty(key); 37. } 38. 39. }
定义LinkProperties,这个类保证了Properties类的有序
1. package com.itheima.shiro.properties; 2. 3. import java.io.*; 4. import java.util.ArrayList; 5. import java.util.Enumeration; 6. import java.util.List; 7. import java.util.Properties; 8. 9. 10. /** 11. * @Description 有序Properties类 12. */ 13. 14. public class LinkProperties extends Properties{ 15. 16. /** serialVersionUID */ 17. private static final long serialVersionUID = 7573016303908223266L; 18. 19. private List<Object> keyList = new ArrayList<Object>(); 20. 21. /** 22. * 默认构造方法 23. */ 24. public LinkProperties() { 25. 26. } 27. 28. /** 29. * 从指定路径加载信息到Properties 30. * @param path 31. */ 32. public LinkProperties(String path) { 33. try { 34. InputStream is = new FileInputStream(path); 35. this.load(is); 36. } catch (FileNotFoundException e) { 37. e.printStackTrace(); 38. throw new RuntimeException("指定文件不存在!"); 39. } catch (IOException e) { 40. e.printStackTrace(); 41. } 42. } 43. 44. /** 45. * 重写put方法,按照property的存入顺序保存key到keyList,遇到重复的后者将覆盖前者。 46. */ 47. @Override 48. public synchronized Object put(Object key, Object value) { 49. this.removeKeyIfExists(key); 50. keyList.add(key); 51. return super.put(key, value); 52. } 53. 54. 55. /** 56. * 重写remove方法,删除属性时清除keyList中对应的key。 57. */ 58. @Override 59. public synchronized Object remove(Object key) { 60. this.removeKeyIfExists(key); 61. return super.remove(key); 62. } 63. 64. /** 65. * keyList中存在指定的key时则将其删除 66. */ 67. private void removeKeyIfExists(Object key) { 68. keyList.remove(key); 69. } 70. 71. /** 72. * 获取Properties中key的有序集合 73. * @return 74. */ 75. public List<Object> getKeyList() { 76. return keyList; 77. } 78. 79. /** 80. * 保存Properties到指定文件,默认使用UTF-8编码 81. * @param path 指定文件路径 82. */ 83. public void store(String path) { 84. this.store(path, "UTF-8"); 85. } 86. 87. /** 88. * 保存Properties到指定文件,并指定对应存放编码 89. * @param path 指定路径 90. * @param charset 文件编码 91. */ 92. public void store(String path, String charset) { 93. if (path != null && !"".equals(path)) { 94. try { 95. OutputStream os = new FileOutputStream(path); 96. BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os, charset)); 97. this.store(bw, null); 98. bw.close(); 99. } catch (FileNotFoundException e) { 100. e.printStackTrace(); 101. } catch (IOException e) { 102. e.printStackTrace(); 103. } 104. } else { 105. throw new RuntimeException("存储路径不能为空!"); 106. } 107. } 108. 109. /** 110. * 重写keys方法,返回根据keyList适配的Enumeration,且保持HashTable keys()方法的原有语义, 111. * 每次都调用返回一个新的Enumeration对象,且和之前的不产生冲突 112. */ 113. @Override 114. public synchronized Enumeration<Object> keys() { 115. return new EnumerationAdapter<Object>(keyList); 116. } 117. 118. /** 119. * List到Enumeration的适配器 120. */ 121. private class EnumerationAdapter<T> implements Enumeration<T> { 122. private int index = 0; 123. private final List<T> list; 124. private final boolean isEmpty; 125. 126. public EnumerationAdapter(List<T> list) { 127. this.list = list; 128. this.isEmpty = list.isEmpty(); 129. } 130. 131. public boolean hasMoreElements() { 132. //isEmpty的引入是为了更贴近HashTable原有的语义,在HashTable中添加元素前调用其keys()方法获得一个Enumeration的引用, 133. //之后往HashTable中添加数据后,调用之前获取到的Enumeration的hasMoreElements()将返回false,但如果此时重新获取一个 134. //Enumeration的引用,则新Enumeration的hasMoreElements()将返回true,而且之后对HashTable数据的增、删、改都是可以在 135. //nextElement中获取到的。 136. return !isEmpty && index < list.size(); 137. } 138. 139. public T nextElement() { 140. if (this.hasMoreElements()) { 141. return list.get(index++); 142. } 143. return null; 144. } 145. 146. } 147. }
查看shirocConfig
加载完整之后交于ShiroFilterFactoryBean使用setFilterChainDefinitionMap使得过滤生效
【4】自定义过滤器
上面我们使用了shiro的默认过滤器,但是由于业务需求,咱们可能要定义自己的过滤器,那么咱们定义呢?
这里我们先查看RolesAuthorizationFilter
分析:改源码表示,例如:/admin/order= roles["admin, root"] ,只有当放问该接口同时具备admin和root两种角色时,才可以被访问。
【5】自定义过滤器使用
【5.1】需求
1、实现只要有其中一个角色,则可访问对应路径
【5.2】RolesOrAuthorizationFilter
新建filter层,新建类RolesOrAuthorizationFilter
1. package com.itheima.shiro.filter; 2. 3. import org.apache.shiro.subject.Subject; 4. import org.apache.shiro.util.CollectionUtils; 5. import org.apache.shiro.web.filter.authz.AuthorizationFilter; 6. 7. import javax.servlet.ServletRequest; 8. import javax.servlet.ServletResponse; 9. import java.io.IOException; 10. import java.util.Set; 11. 12. /** 13. * @Description:角色或关系 14. */ 15. public class RolesOrAuthorizationFilter extends AuthorizationFilter { 16. 17. //TODO - complete JavaDoc 18. 19. @SuppressWarnings({"unchecked"}) 20. public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException { 21. 22. Subject subject = getSubject(request, response); 23. String[] rolesArray = (String[]) mappedValue; 24. 25. if (rolesArray == null || rolesArray.length == 0) { 26. //no roles specified, so nothing to check - allow access. 27. return true; 28. } 29. 30. Set<String> roles = CollectionUtils.asSet(rolesArray); 31. //循环roles判断只要有角色则返回true 32. for (String role : roles) { 33. if(subject.hasRole(role)){ 34. return true; 35. } 36. } 37. return false; 38. } 39. 40. }
【5.3】编辑ShiroConfig
在ShiroConfig类中添加如下内容
1. /** 2. * @Description 自定义过滤器定义 3. */ 4. private Map<String, Filter> filters() { 5. Map<String, Filter> map = new HashMap<String, Filter>(); 6. map.put("role-or", new RolesOrAuthorizationFilter()); 7. return map; 8. } 9. 10. /** 11. * @Description Shiro过滤器 12. */ 13. @Bean("shiroFilter") 14. public ShiroFilterFactoryBean shiroFilterFactoryBean(){ 15. ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); 16. shiroFilter.setSecurityManager(defaultWebSecurityManager()); 17. //使自定义过滤器生效 18. shiroFilter.setFilters(filters()); 19. shiroFilter.setFilterChainDefinitionMap(filterChainDefinition()); 20. shiroFilter.setLoginUrl("/login"); 21. shiroFilter.setUnauthorizedUrl("/login"); 22. return shiroFilter; 23. }
【2.2.3】编辑authentication.properties
#静态资源不过滤
/static/**=anon
#登录链接不过滤
/login/**=anon
#访问/resource/**需要有admin的角色
/resource/**=role-or[admin]
#其他链接是需要登录的
/**=authc
7、注解方式鉴权
【1】注解介绍
以下为常用注解
注解 | 说明 |
@RequiresAuthentication | 表明当前用户需是经过认证的用户 |
@ RequiresGuest | 表明该用户需为”guest”用户 |
@RequiresPermissions | 当前用户需拥有指定权限 |
@RequiresRoles | 当前用户需拥有指定角色 |
@ RequiresUser | 当前用户需为已认证用户或已记住用户 |
例如RoleAction类中我们添加
1. /** 2. *@Description: 跳转到角色的初始化页面 3. */ 4. @RequiresRoles(value ={"SuperAdmin","dev"},logical = Logical.OR) 5. @RequestMapping(value = "listInitialize") 6. public ModelAndView listInitialize(){ 7. return new ModelAndView("/role/role-listInitialize"); 8. }