SpringSecurity(2)


好久没有写了,之前只写了一半,我是一边开发一边写Blog一边上班,所以真心没有那么多时间来维护Blog,项目已经开发到编写逻辑及页面部分了,框架基本上已经搭建好不会再修改了,数据库也扩充了好多了。目前前端的技术框架使用的是BootStrap,集成了几个不错的插件这边列举一下,给大家做一个参考:

  1. select2:一个下拉框的优化组件,支持多级,支持模糊匹配等等,而且也非常美观,与Bootstrap其他控件可完美兼容;
  2. iCheck:一个checkBox和radios的优化组件,支持各种事件回调多种配色方案,与Bootstrap其他控件完美兼容;
  3. artTemplate:这个是腾讯的模板渲染框架,好用,语法简单,类似JSTL,我表示非常推荐;
  4. krpano:这是一个付费的插件,用于制作并可在web上显示720全景照片以及VR模式;【付费插件,价格不菲,不是打广告,我也是找不到更好的才用它,我自己也没有去买正版,不是做上线网站,所以也没打算买。】

  好了,现在还要继续讲解Security的集成工作。

SpringSecurity配置文件


目录:resource/config/spring,文件名:applicationContext-security.xml

  1. <sec:logout invalidate-session="true" logout-url="/logout.do" logout-success-url="/"/>

继续上一篇文章,接下来要讲的就是这个登出的配置了。

  • invalidate-session:这个配置的意思是,当用户登出后,是否将session失效;
  • logout-url:这个自然是登出的url地址了。
  • logout-success-url:当顺利登出后,浏览器跳转的地址,我这样设置是跳转到首页。
  1.   <!--session管理及单点登录-->
  2. <sec:session-management session-authentication-strategy-ref="concurrentSessionControlStrategy"/>
  3.   <!--session管理器 start-->
  4. <bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
  5. <constructor-arg name="sessionRegistry" ref="sessionRegistry"/>
  6. <constructor-arg name="expiredUrl" value="/user/timeout"/>
  7. </bean>
  8.  
  9. <bean id="concurrentSessionControlStrategy"
  10. class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
  11. <constructor-arg name="sessionRegistry" ref="sessionRegistry"/>
  12. <property name="maximumSessions" value="1"/>
  13. </bean>
  14.  
  15. <bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl"/>
  16. <!--session管理器 end-->

  这个是单点登录的管理配置,这个简单说一下吧,expiredUrl这个参数呢,是当session失效之后,页面的跳转地址。maximunSessions指的是最大的session数,如果是限制账号只能单点登录的话,自然要配置为“1”。而sessionRegistry这个是Spring自带实现,我就不多解释了,大家可以自己去看SessionRegistryImpl这个实现类。

  1. <!--资源拦截器配置-->
  2. <sec:custom-filter ref="filterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR"/>
  3. <sec:custom-filter ref="concurrencyFilter" position="CONCURRENT_SESSION_FILTER"/>
  4.  
  5. <!--资源拦截器 start-->
  6.    <bean id="filterSecurityInterceptor"
  7.    class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
  8.    <property name="accessDecisionManager" ref="accessDecisionManager"/>
  9.   <property name="authenticationManager" ref="myAuthenticationManager"/>
  10.   <property name="securityMetadataSource" ref="resourceSecurityMetadataSource"/>
  11.   </bean>

  第一个是资源拦截器,可以看得见,这是一个Filter。第二个是刚才设置的单点登录Filter。顺带讲一下,我不知道是为什么,配置的第一个Filter点击ref名字的时候,可以自动链接跳转,但是后面添加的Filter都统统会提示找不到,但实际上是生效的就是了。

  然后来讲讲资源拦截器中的三个属性:

  1. accessDecisionManager:决策管理器,这个上节有讲过,此处略过;
  2. authenticationManager:认证管理器,主要用于验证用户密码的。这个有专门的配置,一会儿说。
  3. securityMetadataSource:权限验证资源,主要是从数据库中读取资源和权限的对应关系。咱们这个类主要是用于记录“资源访问权限”,还有关于方法的访问权限,就是后面的methodSecurityMetadataSource.方法的访问权限主要是以Spring的AOP模式去做的。

  然后来说说,认证管理器的配置:

  1. <!--认证管理器-->
  2. <sec:authentication-manager alias="myAuthenticationManager">
  3. <sec:authentication-provider ref="daoAuthenticationProvider"/>
  4. </sec:authentication-manager>
  5.  
  6.   <bean id="daoAuthenticationProvider"
  7. class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
  8. <property name="messageSource" ref="messageSource"/>
  9. <property name="passwordEncoder" ref="messageDigestPasswordEncoder"/>
  10. <property name="userDetailsService" ref="cachingUserDetailsService"/>
  11. <property name="saltSource" ref="saltSource"/>
  12. <property name="hideUserNotFoundExceptions" value="false"/>
  13. </bean>
  14.  
  15. <!--认证处理服务-->
  16. <bean id="cachingUserDetailsService"
  17. class="org.springframework.security.config.authentication.CachingUserDetailsService">
  18. <constructor-arg name="delegate" ref="webUserDetailsService"/>
  19. <property name="userCache">
  20. <bean class="org.springframework.security.core.userdetails.cache.EhCacheBasedUserCache">
  21. <property name="cache" ref="userEhCacheFactory"/>
  22. </bean>
  23. </property>
  24. </bean>

这个是认证管理器的配置,其中daoAuthenticationProvider主要用作与认证时查询数据库获取数据库存储的认证信息,比如用户名对应的密码。

messageSource是用于国际化的,这个你们看着配,非必要功能。

passwordEncoder,主要是用于密码加密的,

userDetailsService,这个是用于查找用户信息的类,

saltSource,这个是加密盐值,这个情况是这样子,我们存在数据库中的密码,向来不是明文,都是密文存储,所以在访问密码的时候,都是将用户的密码进一步的加密后再跟系统数据库中的值进行比较,盐值的概念,我不知道怎么解释,给我的理解就是有它进行加密的话会更安全。

hideUserNotFoundException,这个就跟字面意思一样,因此找不到用户的异常,实际上这个异常不应该被隐藏,而是需要抛出,然后错误信息直接反馈到前端页面上,提示用户找不到用户名。

接下来我们来讲解一下这几个属性对应的类,然而要涉及到另一个配置文件,因为这些东西,不仅仅属于SpringSecurity,而更广泛的适用于整个框架中,作为一种Service的角色来使用。

SpringService配置文件


目录:resource/config/spring,文件名:applicationContext-service.xml

  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. xmlns:context="http://www.springframework.org/schema/context"
  5. 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">
  6.  
  7. <!--扫描service-->
  8. <context:component-scan base-package="com.magic.rent.service"/>
  9. <!--注册统一异常控制-->
  10. <bean id="exception" class="com.magic.rent.exception.exhandler.CustomExceptionHandler"/>
  11.    <!--MD5加密-->
  12. <bean id="messageDigestPasswordEncoder"
  13. class="org.springframework.security.authentication.encoding.MessageDigestPasswordEncoder">
  14. <constructor-arg name="algorithm" value="MD5"/>
  15. </bean>
  16. <!--国际化配置-->
  17. <bean id="messageSource"
  18. class="org.springframework.context.support.ResourceBundleMessageSource">
  19. <property name="basename" value="messages"/>
  20. </bean>
  21. <bean id="messageSourceAccessor" class="org.springframework.context.support.MessageSourceAccessor">
  22. <constructor-arg ref="messageSource"/>
  23. </bean>
  24. </beans>
  • 第一行配置自然是扫描Service组件,这个我就不说了,Spring的基本配置常识。
  • 统一异常控制,这个呢就是前面第三篇文章中所提到的统一异常控制的类,可以用注解去注册,也可以像我这样用配置文件的方式,这个到没有什么区别,我之所以没有用注解,而是单独写一行配置,是因为我把Exception跟Service包区分开了,看我之前的目录结构就知道,这样比较方便我维护自定义异常种类而已。
  • MD5加密,这就是我们SpringSecurity的密码加密类,用于加密密码的,其中可以看到我们在属性algorithm中,使用MD5加密。
  • messageSource这个就是我说的国际化配置,其中basename指向的就是国际化配置的资源文件,如下图:

  所有的中文,都要转换成UTF-8的编码,这个文件,在SpringSecurity中又自带的,可以直接拿来用,地址是:

  1. org/springframework/security/spring-security-core/4.1.3.RELEASE/spring-security-core-4.1.3.RELEASE.jar!/org/springframework/security/messages_zh_CN.properties

  然后这边我写了一个转换的工具类:

  1. package com.magic.rent.util;
  2.  
  3. import java.io.BufferedReader;
  4. import java.io.IOException;
  5. import java.io.InputStreamReader;
  6.  
  7. /**
  8. * 创建者: wuxinzhe 创建时间: 16/10/6
  9. * 类说明: UTF-8的中文转换类
  10. */
  11. public class UTF8Util {
  12. /**
  13. * "/"分隔符
  14. *
  15. * @param str
  16. * @return
  17. */
  18. public static String GBK2Unicode(String str) {
  19. StringBuffer result = new StringBuffer();
  20. for (int i = 0; i < str.length(); i++) {
  21. char chr = str.charAt(i);
  22. if (!isNeedConvert(chr)) {
  23. result.append(chr);
  24. continue;
  25. }
  26. result.append("\\u" + Integer.toHexString((int) chr));
  27. }
  28. return result.toString();
  29. }
  30.  
  31. public static boolean isNeedConvert(char para) {
  32. return ((para & (0x00FF)) != para);
  33. }
  34.  
  35. /**
  36. * &#分隔符
  37. *
  38. * @param str
  39. * @return
  40. */
  41. public static String GBK2Unicode2(String str) {
  42. StringBuffer result = new StringBuffer();
  43. for (int i = 0; i < str.length(); i++) {
  44. char chr = str.charAt(i);
  45. result.append("&#" + Integer.toString((int) chr) + ";");
  46. }
  47. return result.toString();
  48. }
  49.  
  50. public static void main(String[] args) throws IOException {
  51. BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
  52. String str = br.readLine();
  53. System.out.println("UTF-8:" + GBK2Unicode(str));
  54. System.out.println("UTF-82:" + GBK2Unicode2(str));
  55. }
  56. }

  运行后在控制台输入你要转换的中文或英文或标点,然后回车后会自动转换成两种不同格式的UTF-8编码。还算挺方便的。

  然后大家还能看到我配置了一个MessageSourceAccess,这个是做什么的呢?这是一个国际化的工具类,非常好用,我随便拿我项目中的一个例子给大家演示:

  可以看到,这个对象有两个参数(这个对象我是写在BaseController当中,通过继承获取,因为这个算是通用的属性。),第一个参数就是像message的配置文件中查找,看是否有配置这个对应的文字,如果没有的话,就采用第二个参数中的值,即默认值,进行返回。到此,我们再回到SpringSecurity的配置文件中,继续讲解:

  1. <!--MD5加密盐值-->
  2. <bean id="saltSource" class="org.springframework.security.authentication.dao.ReflectionSaltSource">
  3. <property name="userPropertyToUse" value="username"/>
  4. </bean>

  这就是盐值的配置了,这个配置的意思,就是说,将用户的用户名,作为加密时的混入MD5的加密中,增强密码的加密强度。当然你也可以不一定用用户名而是其他的什么值。

  然后贴出这个securityMetadataSource的类代码,这个没有什么特殊的,就从数据库中获取数据而已,我留了一个手动刷新的方法,主要是用于后续如果有更新权限的情况下,不需要重启服务器,就可以刷新权限列表,因为我们再启动项目的时候,将数据库中的权限数据一次性加载到内存中,而后续对比权限的时候,实际上只跟内存中的数据对比相当于一个缓存的作用。

  1. package com.magic.rent.service.security;
  2.  
  3. import com.magic.rent.mapper.SysResourcesMapper;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.beans.factory.InitializingBean;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.security.access.ConfigAttribute;
  9. import org.springframework.security.access.SecurityConfig;
  10. import org.springframework.security.web.FilterInvocation;
  11. import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
  12. import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
  13. import org.springframework.security.web.util.matcher.RequestMatcher;
  14. import org.springframework.stereotype.Service;
  15.  
  16. import javax.servlet.http.HttpServletRequest;
  17. import java.util.*;
  18.  
  19. @Service
  20. public class ResourceSecurityMetadataSource implements FilterInvocationSecurityMetadataSource, InitializingBean {
  21.  
  22. private final static List<ConfigAttribute> NULL_CONFIG_ATTRIBUTE = Collections.emptyList();
  23.  
  24. //权限集合
  25. private Map<RequestMatcher, Collection<ConfigAttribute>> requestMap;
  26.  
  27. private static Logger logger = LoggerFactory.getLogger(ResourceSecurityMetadataSource.class);
  28.  
  29. @Autowired
  30. private SysResourcesMapper sysResourcesMapper;
  31.  
  32. public Collection<ConfigAttribute> getAttributes(Object object)
  33. throws IllegalArgumentException {
  34. final HttpServletRequest request = ((FilterInvocation) object).getRequest();
  35.  
  36. Collection<ConfigAttribute> attrs = NULL_CONFIG_ATTRIBUTE;
  37.  
  38. for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMap.entrySet()) {
  39. if (entry.getKey().matches(request)) {
  40. attrs = entry.getValue();
  41. break;
  42. }
  43. }
  44.  
  45. logger.info("请求资源->资源:[{}]->[{}]", request.getRequestURI(), attrs);
  46. return attrs;
  47. }
  48.  
  49. public Collection<ConfigAttribute> getAllConfigAttributes() {
  50. Set<ConfigAttribute> allAttributes = new HashSet<ConfigAttribute>();
  51.  
  52. for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMap.entrySet()) {
  53. allAttributes.addAll(entry.getValue());
  54. }
  55.  
  56. return allAttributes;
  57. }
  58.  
  59. public boolean supports(Class<?> clazz) {
  60. return FilterInvocation.class.isAssignableFrom(clazz);
  61. }
  62.  
  63. private Map<String, String> loadResource() {
  64. Map<String, String> resourceLinkMap = new LinkedHashMap<String, String>();
  65.  
  66. List<Map<String, String>> resourceList = sysResourcesMapper.getURLResourceMapping();
  67.  
  68. for (Map<String, String> resourceMap : resourceList) {
  69. String resourcePath = resourceMap.get("resourcePath");
  70. String authorityMark = resourceMap.get("authorityMark");
  71. if (resourceLinkMap.containsKey(resourcePath)) {
  72. String mark = resourceLinkMap.get("resourcePath");
  73. resourceLinkMap.put(resourcePath, mark + "," + authorityMark);
  74. } else {
  75. resourceLinkMap.put(resourcePath, authorityMark);
  76. }
  77. }
  78.  
  79. return resourceLinkMap;
  80. }
  81.  
  82. protected Map<RequestMatcher, Collection<ConfigAttribute>> bindRequestMap() {
  83. Map<RequestMatcher, Collection<ConfigAttribute>> map =
  84. new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
  85. Map<String, String> resMap = this.loadResource();
  86. for (Map.Entry<String, String> entry : resMap.entrySet()) {
  87. String key = entry.getKey();
  88. map.put(new AntPathRequestMatcher(key), SecurityConfig.createListFromCommaDelimitedString(entry.getValue()));
  89. }
  90. return map;
  91. }
  92.  
  93. public void afterPropertiesSet() throws Exception {
  94. this.requestMap = this.bindRequestMap();
  95. logger.info("资源文件权限参数初始化:资源列表[{}]", requestMap);
  96. }
  97.  
  98. /**
  99. * 手动刷新资源
  100. */
  101. public void refreshResuorceMap() {
  102. this.requestMap = this.bindRequestMap();
  103. }
  104.  
  105. }

  接着来讲一讲cachingUserDetailsService:认证处理服务,这个服务看它的名字就知道,它涉及到了缓存,所以我们加入了缓存的功能,因为登录这种操作,一般情况下肯定不会只登录一次就在不登录了,甚至一天可能会登录好几次,那用缓存的方式,减少数据库的访问,能提高每次验证速度。

  我们可以看到有一个userCache的属性,这个属性就直接连接着userEhCacheFactory这个对象,我们的缓存,用的是EhCache。缓存的配置部分,我还没讲到,下一篇将会作出说明,简单地说,就是通过这个userEhCacheFactory工厂对象,来获取缓存对象。

  另外可以看到构造器中还有一个参数是:delegate,指向的是一个WebUserDetailService,这个类是要自己自定义的:

  1. package com.magic.rent.service.security;
  2.  
  3. /**
  4. *
  5. * 创建者: wu 创建时间: 16/9/23
  6. * 类说明: 用于获取用户角色下的所有权限
  7. */
  8.  
  9. import com.magic.rent.mapper.SysAuthoritiesMapper;
  10. import com.magic.rent.mapper.SysRolesMapper;
  11. import com.magic.rent.mapper.SysUsersMapper;
  12. import com.magic.rent.pojo.SysAuthorities;
  13. import com.magic.rent.pojo.SysRoles;
  14. import com.magic.rent.pojo.SysUsers;
  15. import org.slf4j.Logger;
  16. import org.slf4j.LoggerFactory;
  17. import org.springframework.beans.factory.annotation.Autowired;
  18. import org.springframework.context.MessageSource;
  19. import org.springframework.context.support.MessageSourceAccessor;
  20. import org.springframework.security.core.GrantedAuthority;
  21. import org.springframework.security.core.authority.SimpleGrantedAuthority;
  22. import org.springframework.security.core.userdetails.*;
  23. import org.springframework.stereotype.Service;
  24.  
  25. import java.util.*;
  26.  
  27. @Service
  28. public class WebUserDetailsService implements UserDetailsService {
  29.  
  30. @Autowired
  31. private SysUsersMapper sysUsersMapper;
  32.  
  33. @Autowired
  34. private SysRolesMapper sysRolesMapper;
  35.  
  36. @Autowired
  37. private SysAuthoritiesMapper sysAuthoritiesMapper;
  38.  
  39. @Autowired
  40. private MessageSourceAccessor messageSourceAccessor;
  41.  
  42. private static Logger logger = LoggerFactory.getLogger(WebUserDetailsService.class);
  43.  
  44. public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
  45. SysUsers sysUsers = null;
  46. try {
  47. //从数据中查找数据
  48. sysUsers = sysUsersMapper.selectByUserName(s);
  49. } catch (Exception e) {
  50. e.printStackTrace();
  51. }
  52. //如果查找不到用户信息,则抛出异常
  53. if (sysUsers == null) {
  54. throw new UsernameNotFoundException(
  55. messageSourceAccessor.getMessage("UserDetailsService.userNotFount", "用户未找到!"));
  56. }
  57. //查询用户角色
  58. sysUsers.setSysRoles(sysRolesMapper.selectRolesByUserId(sysUsers.getUserId()));
  59.  
  60. //查询并封装该用户具有什么权限
  61. Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
  62. //用于过滤重复的权限
  63. List<String> preAuthorityMarks = new ArrayList<String>();
  64. if (sysUsers.getSysRoles() != null && !sysUsers.getSysRoles().isEmpty()) {
  65. //遍历用户所具有的所有角色
  66. for (SysRoles role : sysUsers.getSysRoles()) {
  67. //根据角色查询单独角色所具有的权限
  68. List<SysAuthorities> sysAuthoritiesList = sysAuthoritiesMapper.selectByRole(role);
  69. //将权限封装用于后续做判断
  70. for (SysAuthorities sysAuthority : sysAuthoritiesList) {
  71. //过滤已经存在的权限
  72. if (preAuthorityMarks.contains(sysAuthority.getAuthorityMark())) {
  73. //过滤
  74. continue;
  75. } else {
  76. //加入用于过滤的集合中
  77. preAuthorityMarks.add(sysAuthority.getAuthorityMark());
  78. //封装如权限集合中
  79. GrantedAuthority ga = new CustomGrantedAuthority(sysAuthority.getAuthorityMark());
  80. authorities.add(ga);
  81. }
  82. }
  83.  
  84. }
  85. }
  86. //装载权限列表
  87. sysUsers.setAuthorities(authorities);
  88. logger.info("读取用户角色:账户名[{}]-权限[{}]", s, sysUsers.getAuthorities().toString());
  89. //拼装SysUserLoginDetails对象
  90. return sysUsers;
  91. }
  92. }

其中,我们有涉及到另一个类,就是CustomGrantedAuthority,而这个类也是自定义的,可以这样写的:

  1. package com.magic.rent.service.security;
  2.  
  3. import org.springframework.security.core.GrantedAuthority;
  4. import org.springframework.stereotype.Service;
  5. import org.springframework.util.Assert;
  6.  
  7. import java.io.Serializable;
  8.  
  9. /**
  10. *
  11. * 创建者: wuxinzhe 创建时间: 16/10/6
  12. * 类说明:用于封装权限对象
  13. */
  14. public class CustomGrantedAuthority implements GrantedAuthority, Serializable {
  15.  
  16. private static final long serialVersionUID = 9188347583387457302L;
  17.  
  18. private final String authority;
  19.  
  20. public CustomGrantedAuthority(String role) {
  21. Assert.hasText(role, "A granted authority textual representation is required");
  22. this.authority = role;
  23. }
  24.  
  25. public String getAuthority() {
  26. return authority;
  27. }
  28.  
  29. public boolean equals(Object obj) {
  30. if (this == obj) {
  31. return true;
  32. }
  33.  
  34. if (obj instanceof CustomGrantedAuthority) {
  35. return authority.equals(((CustomGrantedAuthority) obj).authority);
  36. }
  37.  
  38. return false;
  39. }
  40.  
  41. public int hashCode() {
  42. return this.authority.hashCode();
  43. }
  44.  
  45. public String toString() {
  46. return this.authority;
  47. }
  48. }

  接着我们讲最后一部分,就是方法拦截器的配置了。

  1.   <!--方法拦截器 start-->
  2. <bean id="methodSecurityInterceptor"
  3. class="org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor">
  4. <property name="accessDecisionManager" ref="accessDecisionManager"/>
  5. <property name="authenticationManager" ref="myAuthenticationManager"/>
  6. <property name="securityMetadataSource" ref="methodSecurityMetadataSource"/>
  7. </bean>
  8. <aop:config>
  9. <aop:advisor advice-ref="methodSecurityInterceptor" pointcut="execution(* com.magic.rent.service.*.*(..))"
  10. order="1"/>
  11. </aop:config>
  12. <!--方法拦截器 end-->

  基本属性我就不再阐述了。

  正如前面所说,方法层级的权限验证,主要是通过AOP的方式来实现的,所以有了关于AOP的配置。

  主要不同在于methodSecurityMetadataSource这个类:

  1. package com.magic.rent.service.security;
  2.  
  3. import com.magic.rent.mapper.SysResourcesMapper;
  4. import com.magic.rent.pojo.MethodKey;
  5. import org.slf4j.Logger;
  6. import org.slf4j.LoggerFactory;
  7. import org.springframework.beans.factory.InitializingBean;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.security.access.ConfigAttribute;
  10. import org.springframework.security.access.SecurityConfig;
  11. import org.springframework.security.access.method.AbstractMethodSecurityMetadataSource;
  12. import org.springframework.stereotype.Service;
  13.  
  14. import java.lang.reflect.Method;
  15. import java.util.*;
  16.  
  17. /**
  18. * Created by wuxinzhe on 16/9/25.
  19. */
  20. @Service
  21. public class MethodSecurityMetadataSource extends AbstractMethodSecurityMetadataSource implements InitializingBean {
  22. private final static List<ConfigAttribute> NULL_CONFIG_ATTRIBUTE = Collections.emptyList();
  23.  
  24. private final static String RES_KEY = "resourcePath";
  25. private final static String AUTH_KEY = "authorityMark";
  26.  
  27. private Map<MethodKey, Collection<ConfigAttribute>> requestMap;
  28.  
  29. private static Logger logger = LoggerFactory.getLogger(MethodSecurityMetadataSource.class);
  30.  
  31. @Autowired
  32. private SysResourcesMapper sysResourcesMapper;
  33.  
  34. /**
  35. * 根据方法获取到访问方法所需要的权限
  36. *
  37. * @param method 访问的方法
  38. * @param targetClass 方法所属的类
  39. */
  40.  
  41. public Collection<ConfigAttribute> getAttributes(Method method,
  42. Class<?> targetClass) {
  43. MethodKey key = new MethodKey(method);
  44. Collection<ConfigAttribute> attrs = NULL_CONFIG_ATTRIBUTE;
  45.  
  46. for (Map.Entry<MethodKey, Collection<ConfigAttribute>> entry : requestMap.entrySet()) {
  47. if (entry.getKey().equals(key)) {
  48. attrs = entry.getValue();
  49. break;
  50. }
  51. }
  52. logger.info("获取Method-资源:[{}]->[{}]", key.getFullMethodName(), attrs);
  53. return attrs;
  54. }
  55.  
  56. /**
  57. * 获取到所有方法对应的权限集合
  58. */
  59. public Collection<ConfigAttribute> getAllConfigAttributes() {
  60. Set<ConfigAttribute> allAttributes = new HashSet<ConfigAttribute>();
  61.  
  62. for (Map.Entry<MethodKey, Collection<ConfigAttribute>> entry : requestMap.entrySet()) {
  63. allAttributes.addAll(entry.getValue());
  64. }
  65.  
  66. return allAttributes;
  67. }
  68.  
  69. /**
  70. * 初始化方法权限对应集合,绑定方法权限集合
  71. */
  72. public void afterPropertiesSet() throws Exception {
  73. this.requestMap = this.bindRequestMap();
  74. }
  75.  
  76. /**
  77. * 从数据库中获取方法及权限对应信息
  78. *
  79. * @return
  80. */
  81. private Map<String, String> loadMethod() {
  82. Map<String, String> resMap = new LinkedHashMap<String, String>();
  83. List<Map<String, String>> list = this.sysResourcesMapper.getMethodResourceMapping();
  84.  
  85. for (Map<String, String> map : list) {
  86. String resourcePath = map.get(RES_KEY);
  87. String authorityMark = map.get(AUTH_KEY);
  88.  
  89. if (resMap.containsKey(resourcePath)) {
  90. String mark = resMap.get(resourcePath);
  91. resMap.put(resourcePath, mark + "," + authorityMark);
  92. } else {
  93. resMap.put(resourcePath, authorityMark);
  94. }
  95. }
  96.  
  97. return resMap;
  98. }
  99.  
  100. /**
  101. * 封装从数据库中获取的方法权限集合
  102. *
  103. * @return
  104. */
  105. public Map<MethodKey, Collection<ConfigAttribute>> bindRequestMap() {
  106. Map<MethodKey, Collection<ConfigAttribute>> resMap =
  107. new LinkedHashMap<MethodKey, Collection<ConfigAttribute>>();
  108.  
  109. Map<String, String> map = this.loadMethod();
  110. for (Map.Entry<String, String> entry : map.entrySet()) {
  111. MethodKey key = new MethodKey(entry.getKey());
  112. resMap.put(key, SecurityConfig.createListFromCommaDelimitedString(entry.getValue()));
  113. }
  114.  
  115. return resMap;
  116. }
  117.  
  118. }

  这个类暂时忘记了增加手动刷新内存中权限列表的方法,以后再弄吧,反正现在还没有开发管理页面,写法跟上面资源权限列表的那个类是一样的。

  好啦,到此,万恶的SpringSecurity就配置完了。

顺带的,贴几张,前端截图,嘻嘻嘻,做网站吗,忙活了半天,肯定要看到一个直观的结果撒~

【JavaWeb】Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架(五)的更多相关文章

  1. 【JavaWeb】Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架(三)

    Spring+SpringMVC MVC呢,现在似乎越来越流行使用SpringMVC框架,我自己用的感觉,是非常好,确实很舒服,配置一开始是麻烦了一点点,但是后续的开发真的是很清爽! SpringMV ...

  2. 【JavaWeb】Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架(前言)

    一直希望能够搭建一个完整的,基础Web框架,方便日后接一些外快的时候,能够省时省力,终于花了一周的时间,把这个东西搞定了.特此写下此博客,一来是纪念,二来是希望能够为别人提供方便.顺带说一下,恩,组合 ...

  3. Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架(前言)

    简单介绍一下,本框架的基本功能点: Spring:整个框架的主体部分,这个自不用说. SpringMVC:MVC部分我还是比较喜欢Spring的. MyBatis:选型的时候选择这个ORM主要也是考虑 ...

  4. 【JavaWeb】Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架(四)

    SpringSecurity(1) 其实啊,这部分我是最不想写的,因为最麻烦的也是这部分,真的是非常非常的麻烦.关于SpringSecurity的配置,让我折腾了好半天,网上的配置方式一大把,但总有一 ...

  5. 【JavaWeb】Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架(一)

    Spring+MyBatis 首先要搭建的是Spring+MyBatis的整合框架,毕竟Spring是整个Web框架的核心部位,而数据库操作是一切测试的基础嘛. 目录结构 ━java ┣ contro ...

  6. 【JavaWeb】Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架(二)

    Log4j 这个东西,大家都熟悉,就简单的介绍一下,算是一个抛砖引玉,因为我自己在Log日志的搭建方面,没有什么经验,但这东西确实是非常重要的,日后调Bug没有它基本不可能,如果有朋友有什么比较好的L ...

  7. 【JavaWeb】SSM+SpringSecurity+EhCache+JCaptcha 完整Web基础框架(六)

    Showings 我个人的项目,当前不断地在更新. 我希望做成一个好项目,同时,也是在锻炼自己的技术. 在项目中发现问题,学习知识,是比较可取的一条路子. 这样学习到的知识,虽然分散,但是都很实用,而 ...

  8. Spring+SpringMVC+Mybatis+MAVEN+Eclipse+项目完整环境搭建

    1.新建一个Maven项目,创建父项目. 2.创建子项目模块 3.创建javaWeb项目 4.创建后的项目目录结构 5.Maven文件配置 parent父项目pom.xml文件配置 <?xml ...

  9. javaweb项目-医者天下 (Spring+SpringMVC+MyBatis)

    项目下载地址:http://download.csdn.net/detail/qq_33599520/9826683 项目完整结构图: 项目简介: 医者天下项目是一个基于Spring+SpringMV ...

随机推荐

  1. 5.2 Array类型介绍

    Array类型是数组类型,Array(数组)类型也是引用类型中的一种. js 数组中的每一项可以保存任何类型的数据. js数组的大小/长度是可以动态调整的.如果你往数组中添加数据,数组长度会自动增加. ...

  2. Using View and Data API with Meteor

    By Daniel Du I have been studying Meteor these days, and find that Meteor is really a mind-blowing f ...

  3. 菜单(Menu)的三中创建方式——Android开发之路2

    菜单的三种创建方式 一.OptionsMenu---选项菜单 Android应用中的菜单默认是隐藏的,只有当用户点击手机上的MENU键,系统才会显示菜单.这种菜单叫做选项菜单(Options Menu ...

  4. 自动将指定目录下面的文件转换为UTF-8

    using System; using System.Collections; using System.Collections.Generic; using System.IO; using Sys ...

  5. Html --用简单的<hr>实现多样化分割效果

    最基本的:<hr width=300 size=1 color=#5151A2 align=center noshade>. <!--其中 width 规定线条的长度,还可以是百分比 ...

  6. ASP.NET MVC 5 01 - ASP.NET概述

    本篇目录: ASP.NET 概述 .NET Framework 与 ASP.NET ASP.NET MVC简介 ASP.NET的特色和优势 典型案例 ▁▃▅ ASP.NET概述 ▅▃▁ 目前开发B/S ...

  7. 基于Spring+SpringMVC+Mybatis的Web系统搭建

    系统搭建的配置大同小异,本文在前人的基础上做了些许的改动,重写数据库,增加依据权限的动态菜单的实现,也增加了后台返回json格式数据的配置,详细参见完整源码. 主要的后端架构:Spring+Sprin ...

  8. 烂泥:centos6 yum方式升级内核

    本文由ilanniweb提供友情赞助,首发于烂泥行天下 想要获得更多的文章,可以关注我的微信ilanniweb 最近没有时间好久没有写文章了,今天由于需要安装docker学习虚拟容器的知识,需要升级O ...

  9. 萌新笔记——linux下(ubuntu)反删除(误删恢复)与回收站制作

    刚刚有个小伙伴不小心删了他写了好几的天代码,为他心疼之余帮他找回了文件. 想到我之前也常常误删一些文件,就干脆分享一下我的反删除方法,并说说我做的回收站(好low的,求大神指点) 首先是反删除软件ex ...

  10. 【转】[fix] Wireshark error: There are no interfaces on which a capture can be done. on Mac OS X

    I got the following error message when trying to open a network interface for capture using Wireshar ...