spring security 應用實例

  1. 開篇說明java

最近工做有權限控制的需求,因此看了一下spring的security,它提供了很好的安全服務;spring

參考文章:http://peiquan.blog.51cto.com/7518552/1384168 ;數據庫

在這裏我使用第三種權限控制方法,即將用戶,權限,資源使用數據庫存儲,並自定義過濾器,在配置文件裏進行相應配置。安全

2、數據準備服務器

--權限表mvc

CREATE TABLE `authorities` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `authority` varchar(20) DEFAULT NULL,
  `uid` int(11) DEFAULT NULL,  //用戶id
  PRIMARY KEY (`id`)
) ;jsp

INSERT INTO `authorities` VALUES ('1', 'ROLE_ADMIN', '1');
INSERT INTO `authorities` VALUES ('2', 'ROLE_USER', '2');ide

--用戶表(密碼爲123,這裏已加密)工具

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) DEFAULT NULL,
  `password` varchar(60) DEFAULT NULL,
  `enabled` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
);ui

INSERT INTO `users` VALUES ('1', 'admin', 86888061b399e74e30eeead8c7aab922, '1');
INSERT INTO `users` VALUES ('2', 'user', '368703df04cc8d60e2f494a5c244e45a', '1');

--資源表

CREATE TABLE `demo_resources` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `resource_name` varchar(100) NOT NULL,
  `resource_type` varchar(100) NOT NULL,
  `resource_content` varchar(200) NOT NULL,
  `resource_desc` varchar(200) NOT NULL,
  `enabled` int(2) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `resource_name` (`resource_name`),
  KEY `resource_name_2` (`resource_name`)
);

INSERT INTO `demo_resources` VALUES ('1', '全部資源', 'requesturl', '/**', '全部頁面',  '1');
INSERT INTO `demo_resources` VALUES ('2', '管理員資源', 'requesturl', '/admin.jsp', '進入管理員頁面',  '1');
INSERT INTO `demo_resources` VALUES ('3', 'user資源', 'requesturl', '/', 'user能進入首頁',  '1');

--資源與權限關聯表

CREATE TABLE `resource_authority` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `rid` int(11) DEFAULT NULL,   //資源id
  `aid` int(11) DEFAULT NULL,  //權限id
  PRIMARY KEY (`id`)
);

INSERT INTO `resource_authority` VALUES ('1', '1', '1');
INSERT INTO `resource_authority` VALUES ('2', '2', '1');
INSERT INTO `resource_authority` VALUES ('3', '3', '2');

上面的數聽說明:

       1) admin角色的用戶可以訪問全部資源(/**,固然我加/admin.jsp這個有點多餘,不過不要緊) ;

       2) user角色的用戶只能進入首頁(/);

3、security配置

<http pattern="/login.jsp" security="none" />
  <http auto-config="true" access-denied-page="/403.jsp">
     <form-login login-page="/login.jsp" />  
     <!-- 自定義filter -->  
    <custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="securityInterceptorFilter" />
  </http>
  
 <!-- 配置認證管理器 -->
 <authentication-manager  alias="authenticationManager">
    <authentication-provider user-service-ref='userDetailsService'>
      <!-- 用戶加密解密類  -->  
            <password-encoder hash="md5">
                <salt-source user-property="username"/>  
            </password-encoder> 
    </authentication-provider>
 </authentication-manager> 
 
 
 <beans:bean id="userDetailsService" class="com.springmvc.security.impl.SpringMvcUserDetailsServiceImpl" />
 
 <!-- PasswordEncoder 密碼接口 --> 
 <beans:bean id="passwdEcoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder"/>
  
 <!-- 元數據提供接口 -->
 <beans:bean id="springMvcInvocationSecurityMetadataSource" class="com.springmvc.security.impl.SpringMvcInvocationSecurityMetadataSourceImpl" >
    
 </beans:bean>
 <!-- 權限抉擇接口 -->
 <beans:bean id="accessDecisionManager" class="com.springmvc.security.impl.DemoAccessDecisionManager"/>
 
 <!-- 自定義過濾器 -->  
 <beans:bean id="securityInterceptorFilter" class="com.springmvc.security.impl.DemoSecurityInterceptor">  
     <beans:property name="securityMetadataSource" ref="springMvcInvocationSecurityMetadataSource"/><!-- FilterInvocationSecurityMetadataSource 接口實現類 -->  
     <beans:property name="authenticationManager" ref="authenticationManager"/><!-- 鑑定管理類 -->  
     <beans:property name="accessDecisionManager" ref="accessDecisionManager"/><!-- AccessDecisionManager 接口實現類 -->  
 </beans:bean>

4、功能說明

    1) springMvcInvocationSecurityMetadataSource

     服務器啓動時,會將數據庫中全部權限和資源提取出來,放在一個map裏,等用戶登陸到該系統時,就會使用到map,從而判斷該用戶是否有這個權限。

 public class SpringMvcInvocationSecurityMetadataSourceImpl implements
  FilterInvocationSecurityMetadataSource {
 private static final Logger logger = LoggerFactory
   .getLogger(SpringMvcInvocationSecurityMetadataSourceImpl.class);
 private SecurityServiceInf securityService;
 @Autowired
 public SpringMvcInvocationSecurityMetadataSourceImpl(
   SecurityServiceInf securityService) {
  this.securityService = securityService;
  initResources();
 }
 // 全部的資源和權限的映射就存在這裏
 private HashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new HashMap<RequestMatcher, Collection<ConfigAttribute>>();
 private Collection<ConfigAttribute> allAttribute = new HashSet<ConfigAttribute>();
 /**
  * 初始化全部的資源,這個會在容器運行的時候的構造方法裏調用
  */
 private void initResources() {
  logger.debug("init SecurityMetadataSource load all resources");
  // 讀取全部的資源,和資源相關聯的的權限
  // 讀取全部權限點
  Collection<AuthorityEntity> allAuthority = securityService
    .getAllAuthority();
  logger.debug("start to convert AUthortiyEntity to SercurityConfig");
  for (AuthorityEntity authEntity : allAuthority) {
   String authString = authEntity.getAuthority();
   logger.debug("add authroity named:[" + authString + "]");
   SecurityConfig attrConfig = new SecurityConfig(authString);
   allAttribute.add(attrConfig);
  }
  // 讀取全部資源
  Collection<ResourceEntity> allResources = securityService
    .findAllResources();
  // 循環全部資源
  for (ResourceEntity resourceEntiry : allResources) {
   // 按照資源查詢和資源相關的權限點
   Collection<AuthorityEntity> authEntities = securityService
     .getAuthorityByResource(resourceEntiry.getId());
   // 把此關係保存到requestMap裏
   // 獲取資源
   String resourceContent = resourceEntiry.getResourceContent();
   // 把url資源轉化爲一個spring的工具類,請求匹配器類
   logger.debug("add new requestmatcher with [" + resourceContent
     + "]");
   RequestMatcher matcher = new AntPathRequestMatcher(resourceContent);
   // 循環權限 定義一個權限的集合,和此資源對應起來,添加到HashMap裏
   Collection<ConfigAttribute> array = new ArrayList<ConfigAttribute>(
     authEntities.size());
   for (AuthorityEntity auth : authEntities) {
    // 轉化權限對象爲SecurityConfig
    SecurityConfig securityConfig = new SecurityConfig(
      auth.getAuthority());
    array.add(securityConfig);
   }
   requestMap.put(matcher, array);
  }
 }
 /**
  * 根據資源獲取須要的權限名稱
  */
 @Override
 public Collection<ConfigAttribute> getAttributes(Object object)
   throws IllegalArgumentException {
  logger.debug("get resource " + object + " authority");
  // 把對象轉化爲請求
  final HttpServletRequest request = ((FilterInvocation) object)
    .getRequest();
  // 循環整個Map 看看有沒有能夠匹配的,若是有匹配的就馬上返回
  Collection<ConfigAttribute> attrHashMap = new HashSet<ConfigAttribute>();
  for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMap
    .entrySet()) {
   if (entry.getKey().matches(request)) {
    logger.debug("request matches :" + request.getRequestURL());
    attrHashMap.addAll(entry.getValue());
   }
  }
  if (attrHashMap.size() > 0) {
   // 若是有匹配的就轉成ArrayList,而後返回list
   Collection<ConfigAttribute> attr = new ArrayList<ConfigAttribute>(
     attrHashMap);
   return attr;
  }
  logger.debug("request no matches");
  return Collections.emptyList();
 }
 /**
  * 獲取全部權限點
  */
 @Override
 public Collection<ConfigAttribute> getAllConfigAttributes() {
  return this.allAttribute;
 }
 @Override
 public boolean supports(Class<?> clazz) {
  // TODO Auto-generated method stub
  return true;
 }
}

  requestMap裏的數據以下:

 

  2) userDetailsService

  當用戶登陸時,會使用輸入的用戶信息,與數據庫中的比較,用戶名錯誤或密碼錯誤,都會有相應的提示(下面會有介紹),都正確的話,會返回一個user實體。

 public class SpringMvcUserDetailsServiceImpl implements UserDetailsServiceInf {
 @Autowired
 private DemoAuthorityRepository authRepository;
 @Autowired
 private UserRepository demoUserReposiroty;
 @Override
 public UserDetails loadUserByUsername(String username)
   throws UsernameNotFoundException {
  // 讀取用戶
  UsersEntity userEntity = demoUserReposiroty.findByName(username);
  // 讀取權限
  Collection<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
  // 這裏須要從數據庫裏讀取全部的權限點
  Collection<com.springmvc.model.AuthorityEntity> aes = authRepository
    .getAuthorityByUser(userEntity.getId());
  for (AuthorityEntity ae : aes) {
   auths.add(new SimpleGrantedAuthority(ae.getAuthority()));
  }
  User user = new User(userEntity.getUsername(),
    userEntity.getPassword(), true, true, true, true, auths);
  return user;
 }
}

   3) accessDecisionManager

   判斷當前用戶是否擁有訪問該資源的權限。

 public class DemoAccessDecisionManager implements AccessDecisionManager {
 @Override
 public void decide(Authentication authentication, Object object,
   Collection<ConfigAttribute> configAttributes)
   throws AccessDeniedException, InsufficientAuthenticationException {
  if (null == configAttributes) {
   return;
  }
  Iterator<ConfigAttribute> cons = configAttributes.iterator();
  while (cons.hasNext()) {
   ConfigAttribute ca = cons.next();
   String needRole = ((SecurityConfig) ca).getAttribute();
   // gra 爲用戶所被賦予的權限,needRole爲訪問相應的資源應具備的權限
   for (GrantedAuthority gra : authentication.getAuthorities()) {
    if (needRole.trim().equals(gra.getAuthority().trim())) {
     return;
    }
   }
  }
  throw new AccessDeniedException("沒有權限");
 }
 @Override
 public boolean supports(ConfigAttribute attribute) {
  // TODO Auto-generated method stub
  return true;
 }
 @Override
 public boolean supports(Class<?> clazz) {
  // TODO Auto-generated method stub
  return true;
 }
}

5、權限控制

  1) 登陸

    若使用security默認的登陸頁,則登陸時的錯誤提示信息是在spring-security-core包下面的messages.properties等;

    可是通常咱們使用本身的登陸頁,上面security.xml已配置了登錄頁的路徑login.jsp,那麼提示信息就得本身配置了,能夠自定義message_zh_CN.properties,放在根路徑下的message包裏,而後這樣配置:

 <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="defaultEncoding" value="UTF-8" />
    <property name="basenames">
       <list>
          <value>classpath:message/message</value>
       </list>
    </property>
 </bean>

  message_zh_CN.properties信息以下:

 AbstractUserDetailsAuthenticationProvider.badCredentials=\u5BC6\u7801\u4E0D\u6B63\u786E

  security默認提示:壞的憑證,這裏是'密碼不正確',固然你能夠改爲任何提示信息;

  若登陸時用戶名錯誤,返回的信息是no entity found....

  下面使用錯誤密碼登陸,提示信息以下:

 

  比較密文的代碼以下:

 

 

 2) 登陸成功後,訪問資源

      i. 使用admin帳號登陸,而後訪問admin.jsp

   

    

  

     ii. 使用user帳號登陸,而後訪問admin.jsp

  

   

   能夠看到,user無權訪問admin.jsp。

ok,只要權限和資源關係配置好,security會幫咱們自動攔截,實現權限控制。

相關文章
相關標籤/搜索