2012-09-11 149 views
2

正如標題所說,我正在開發一個Web應用程序,從外部應用程序接收用戶認證信息。我的應用程序的彈簧控制器獲取用戶信息並將其存儲在會話中。我想驗證的Spring Security這裏面用戶,然後用他的角色授予/拒絕訪問的URL像春季安全只限於授權。外部認證

<intercept-url pattern="/myprotectedpage*" access="hasRole('rightrole')" /> 

我讀了一些教程談到PRE_AUTH_FILTER和的UserDetailsS​​ervice,但我不能讓的地步。 Spring Security的應用程序生命週期是什麼?涉及哪些類? 我需要一些完整的工作樣本。

+0

請你給我們你想如何給你的用戶進行身份驗證的詳細信息?您可以爲每個用戶隱式設置身份驗證令牌,但我不知道這是否是您想要的。外部應用程序提供什麼? –

+0

我已經試過這段代碼:List authorities = new ArrayList (); authorities.add(new GrantedAuthorityImpl(「u」)); \t UsernamePasswordAuthenticationToken tok = new UsernamePasswordAuthenticationToken(「username」,「password」,authorities); \t SecurityContextHolder.getContext()。setAuthentication(tok);用戶名,密碼和角色的值來自外部應用程序。 – Tostis

+0

您可以嘗試像這樣將SecurityContext添加到會話中。 final HttpSession session = request.getSession(true); session.setAttribute(「SPRING_SECURITY_CONTEXT」,context);使SecurityContextPersistenceFilter知道上下文 –

回答

0

實現保存用戶信息的服務。

@Service 
public class UserAuthenticationInfoService { 

    private final Map<String, UserInfo> infos = new HashMap<String, UserInfo>(); 

    public void addUserInfo(UserInfo userInfo){ 
     infos.put(userInfo.getUsername(), userInfo); 
    } 

    public UserInfo getUserInfo(String username) { 
     return infos.get(username); 
    } 

} 

您的控制器使用您從外部應用程序收到的用戶信息填充UserAuthenticationInfoService服務。

然後實現一個自定義UserDetailsS​​ervice來訪問這些信息。

public class CustomerUserDetailsService implements UserDetailsService { 
    @Autowired 
    private UserAuthenticationInfoService service; 

    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException { 
     UserInfo info = service.getUserInfo(userName); 
     return new User(info.getUsername(), info.getPassword(), info.getAuthorities()); 
    } 
} 

和設置春季安全上下文中使用這個UserDetailsS​​ervice的(你會發現它的春季安全文檔中)

+0

謝謝,它的工作原理,但我無法工作在控制器內填充用戶信息的自動裝配。我使用了一個靜態類,因爲我耗盡了時間。如果千用戶登錄但是從不登出將會發生什麼? UserAuthenticationInfoService將無限增長? – Tostis

+1

看看谷歌的番石榴圖書館,它包含一個緩存生成器,允許你建立基於上次訪問時間或創建時間過期的緩存。 –

1

這裏有很多相同的東西,只需要google正確。

無論如何,我發現的最好的日期(幾乎所有的春天)是Krams,這裏是基本的春季安全。

http://krams915.blogspot.com/2010/12/spring-security-mvc-integration_18.html

對於這裏實現UserDetailService的鏈接

http://krams915.blogspot.in/2012/01/spring-security-31-implement_5023.html

其他一些國家是:

  1. Spring By Example
  2. MK Young
  3. And SpringSource Site itself

編輯

這是我自己的應用程序如何做認證(請注意,我不使用外部身份驗證,我simpply得到DB的細節,但我想這應該不會有太大的一個問題)。

security-context.xml

<?xml version="1.0" encoding="UTF-8"?> 
<beans:beans xmlns="http://www.springframework.org/schema/security" 
    xmlns:beans="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
         http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd"> 

    <global-method-security pre-post-annotations="enabled" jsr250-annotations="enabled" secured-annotations="enabled"> 
    </global-method-security> 

    <http use-expressions="true"> 
     <intercept-url pattern="/favicon.ico" access="permitAll" /> 
     <intercept-url pattern="/static/**" access="permitAll"/> 
     <intercept-url pattern="/login.jsp*" access="permitAll"/> 
     <intercept-url pattern="/Admin/**" access="hasAnyRole('ROLE_SUPER_USER')"/> 
     <intercept-url pattern="/**" access="hasAnyRole('ROLE_USER','ROLE_SUPER_USER','ROLE_ADMIN'"/> 
     <form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1" /> 
     <http-basic/> 
     <logout logout-success-url="/login.jsp"/> 
     <remember-me user-service-ref="loginService"/
    </http> 

    <authentication-manager> 
     <authentication-provider user-service-ref="loginService"> 
     <password-encoder hash="md5"/> 
     </authentication-provider> 
    </authentication-manager> 

    <beans:bean id="loginService" class="com.indyaah.service.LoginService"> 
    </beans:bean> 
    <beans:bean id="authService" class="com.indyaah.service.AuthService" /> 
</beans:beans> 

現在,當你看到我已經指定了一個名爲loginService因爲這是com.indyaah.service.LoginService類豆我的身份驗證提供豆。

爲同一代碼是: PL注意我已經被截斷不必要的代碼

package com.indyaah.service; 
.. 
@Service 
public class LoginService implements UserDetailsService { 

.... 

    /** 
    * Implementation for custom spring security UserDetailsService 
    */ 
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException { 
     logger.debug("Inside get member by username"); 
     if (userName != null) { 
      Member memberVO = memberMapper.getMemberByUsername(userName); 
      if (memberVO != null) { 
       ArrayList<String> authList = memberRolesMapper.getMemberRoles(memberVO.getMemberId()); 

       List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); 
       for (String role : authList) { 
        System.out.println(role); 
        authorities.add(new GrantedAuthorityImpl(role.toString())); 
       } 

       if (memberVO.getEnabled()) { 
        User user = new User(memberVO.getUserName(), memberVO.getPassword(), true, true, true, true, authorities); 
        return user; 
       } else { 
        logger.error("User with login: " + userName + " not Enabled in database. Authentication failed for user "); 
        throw new UsernameNotFoundException("User Not Enabled"); 
       } 
      } else { 
       logger.error("User with login: " + userName + " not found in database. Authentication failed for user "); 
       throw new UsernameNotFoundException("user not found in database"); 
      } 
     } else { 
      logger.error("No User specified in the login "); 
      throw new UsernameNotFoundException("No username specified"); 
     } 

    } 
} 

注意兩件事情在這裏。

  1. 我得到了用戶的詳細信息(在我的情況下,從數據庫,你可能會分差),並把它放在然後由該方法返回到春季安全的新org.springframework.security.core.userdetails.User對象。
  2. 此外,當局(根據我的數據庫架構將數據從DB單獨加載,再次您的場景可能會有所不同),並通過相同的用戶對象將其傳遞給安全性。
0

您可以實施自己的自定義AuthenticationManager和自定義UsernamePasswordAuthenticationFilter。這是簡單的例子,但它可以給你一個想法,還爲您的信息,這是安全上下文:)非常敏感的部分

只需創建豆你spring_security.xml:

<http entry-point-ref="authenticationProcessingFilterEntryPoint" 
     use-expressions="true"> 

     <custom-filter ref="sessionManagementFilter" before="SESSION_MANAGEMENT_FILTER" /> 
     <custom-filter ref="customUsernamePasswordAuthenticationFilter" 
      position="FORM_LOGIN_FILTER" /> 

     <session-management 
      session-authentication-strategy-ref="sas"></session-management> 
    </http> 


    <beans:bean id="authenticationProcessingFilterEntryPoint" 
      class="org.springframework.security.web.authentication.AuthenticationProcessingFilterEntryPoint"> 
      <beans:property name="loginFormUrl" value="/login" /> 
     </beans:bean> 

     <beans:bean id="sas" 
      class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy" /> 

     <beans:bean id="customAuthenticationManager" 
      class="my.package.security.CustomAuthenticationManager" /> 

     <beans:bean id="customUsernamePasswordAuthenticationFilter" 
      class="my.package.security.CustomUsernamePasswordAuthenticationFilter"> 

      <beans:property name="sessionAuthenticationStrategy" 
       ref="sas" /> 

      <beans:property name="authenticationManager" ref="customAuthenticationManager" /> 


      <beans:property name="authenticationSuccessHandler"> 

       <beans:bean 
        class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler"> 

        <beans:property name="defaultTargetUrl" value="/main.xhtml" /> 

       </beans:bean> 

      </beans:property> 

      <beans:property name="authenticationFailureHandler"> 

       <beans:bean 
        class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"> 

        <beans:property name="defaultFailureUrl" value="/login.xhtml" /> 

       </beans:bean> 

      </beans:property> 
     </beans:bean> 
<beans:bean id="sessionManagementFilter" 
     class="org.springframework.security.web.session.SessionManagementFilter"> 
     <beans:constructor-arg name="securityContextRepository" 
      ref="httpSessionSecurityContextRepository" /> 
    </beans:bean> 
    <beans:bean id="httpSessionSecurityContextRepository" 
     class="org.springframework.security.web.context.HttpSessionSecurityContextRepository" /> 

當你實現CustomUsernamePasswordAuthenticationFilter覆蓋驗證並添加您的外部邏輯:

public final class CustomUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter { 

@Override 
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response){ 
CustomAuthentication auth = new CustomAuthentication(); 

      // set details of current user 
      auth.setDetails(new WebAuthenticationDetails(request)); 
      auth.setAuthenticated(true); 
      auth.setUserName(username); 

      // set authentication to current security session 
      LOGGER.info("Setting authentication into existing security context"); 
      SecurityContextHolder.getContext().setAuthentication(auth); 

      // if validation done return generated authentication 
      return auth; 

} 

} 

然後生成驗證對象將由認證管理器進行處理:

public final class CustomAuthenticationManager implements AuthenticationManager { 

    /* 
    * (non-Javadoc) 
    * 
    * @see org.springframework.security.authentication.AuthenticationManager# 
    * authenticate(org.springframework.security.core.Authentication) 
    */ 
    private static final Logger LOGGER = LoggerFactory.getLogger(CustomUsernamePasswordAuthenticationFilter.class); 
    private final BadCredentialsException badCredentialsException = new BadCredentialsException("Invalid username/password"); 
    @Override 
    public Authentication authenticate(Authentication authentication) { 

     //check if user has valid authentication 
     if (authentication == null) { 
      LOGGER.debug("Null authentication"); 
      throw badCredentialsException; 
     } 
     //Check mandatory fields 
     if (!Validator.isValidString((String) authentication.getPrincipal()) || !Validator.isValidString((String) authentication.getCredentials())) { 
      LOGGER.debug("Null/blank username/credential"); 
      throw badCredentialsException; 

     } 
     //Check if there is any role assigned into user 
     if (authentication.getAuthorities() != null && authentication.getAuthorities().size() < 1) { 
      LOGGER.debug("No authority found"); 
      throw badCredentialsException; 
     } 

     //Validate role 
     //IF ROLE VALIDATION REQUIRED YOU CAN HANDLE IT HERE 
     boolean authorityValid = false; 
     LOGGER.info("Validating user authentication. Total grantedAuth size: " + authentication.getAuthorities().size()); 
     for (GrantedAuthority g : authentication.getAuthorities()) { 
      if (!authorityValid) { 
       //Testing purpose one type role available, when exact roles prepared create enum types 
       authorityValid = g.getAuthority().equals("ROLE_LDAP_AUTHENTICATED"); 
      } 
     } 

     //if none of role matching to required throw exception 
     if(!authorityValid){ 
      LOGGER.debug("User has authority but none of them matching"); 
      throw badCredentialsException; 
     } 

     LOGGER.info("Final validation done returning authentication"); 
     return authentication; 
    } 

} 

然後如果需要的話,您可以覆蓋默認的驗證對象也一樣,如果這裏位於動態角色是你處理:

public final class CustomAuthentication implements Authentication { 

     /** 
     * 
     */ 
     private static final long serialVersionUID = 1L; 
     private transient String userName; 
     private transient boolean authenticated; 
     private transient Object details; 
     private static final transient String ROLE = "ROLE_LDAP_AUTHENTICATED"; 

     /* 
     * (non-Javadoc) 
     * 
     * @see java.security.Principal#getName() 
     */ 
     @Override 
     public String getName() { 
      return this.userName; //for dynamic username logic here 
     } 



    //IF ROLES DYNAMICALLY ALLOCATED ASSIGN IT HERE, HERE IS WHERE YOU READ FROM INTERCEPT URL 

     /* 
     * (non-Javadoc) 
     * 
     * @see org.springframework.security.core.Authentication#getAuthorities() 
     */ 
     @Override 
     public Collection<GrantedAuthority> getAuthorities() { 
      Collection<GrantedAuthority> auths = new ArrayList<GrantedAuthority>(); 
      auths.add(new GrantedAuthority() { 

       /** 
       * 
       */ 
       private static final long serialVersionUID = 1L; 

       @Override 
       public String getAuthority() { 
        if (authenticated) { 

         return ROLE; 
        } 
        return null; 
       } 
      }); 
      return auths; 
     } 

     /* 
     * (non-Javadoc) 
     * 
     * @see org.springframework.security.core.Authentication#getCredentials() 
     */ 
     @Override 
     public Object getCredentials() { 
      //TODO: a specific algorithm can be stored 
      return userName + " is ldap authenticated user"; 
     } 

     /* 
     * (non-Javadoc) 
     * 
     * @see org.springframework.security.core.Authentication#getDetails() 
     */ 
     @Override 
     public Object getDetails() { 
      return this.details; 
     } 

     /* 
     * (non-Javadoc) 
     * 
     * @see org.springframework.security.core.Authentication#getPrincipal() 
     */ 
     @Override 
     public Object getPrincipal() { 
      return userName; 
     } 

     /* 
     * (non-Javadoc) 
     * 
     * @see org.springframework.security.core.Authentication#isAuthenticated() 
     */ 
     @Override 
     public boolean isAuthenticated() { 

      return this.authenticated; 
     } 

     /* 
     * (non-Javadoc) 
     * 
     * @see 
     * org.springframework.security.core.Authentication#setAuthenticated(boolean 
     *) 
     */ 
     @Override 
     public void setAuthenticated(boolean arg0) { 
      this.authenticated = arg0; 

     } 

     public String getUserName() { 
      return userName; 
     } 

     public void setUserName(String userName) { 
      this.userName = userName; 
     } 

     public void setDetails(Object details) { 
      this.details = details; 
     } 

    }