2015-10-24 162 views
4

我正在嘗試創建一個應用程序,它將主要使用Spring訪問REST API,並且正在嘗試配置安全性方面。設法提出使用該畫面中的應用程序的實際結構: enter image description hereSpring安全與REST API

  1. 請求可以來自任何平臺,以「abc.com/rest_api/」
  2. 請求將被髮送到第3點或點5。如果用戶已經通過用戶名和密碼進行了認證,那麼請求將根據Token進行驗證,否則將被重定向到數據庫。
  3. 如果用戶名和密碼必須通過數據庫進行身份驗證,則會生成一個令牌並作爲響應發回。
  4. 之後,只有基於令牌的認證才能起作用。

我試圖創建一個基本的結構,我認爲必須做出一個小錯誤,因爲它不按預期工作。

@Configuration 
@EnableWebSecurity 
@EnableWebMvcSecurity 
@EnableGlobalMethodSecurity(securedEnabled=true, prePostEnabled=true) 
public class UserDetailsSecurityConfig extends WebSecurityConfigurerAdapter { 
    @Autowired 
    private NSecurityContextHolder securityContextHolder; 

    @Autowired 
    private NHttpServletRequestBinder<Authentication> authenticationBinder; 

    public static final String DEF_USERS_BY_USERNAME_QUERY 
      = "SELECT user "; 


public static final String GROUPS_BY_USERNAME_QUERY = 
     "SELECT groups by user"; 
public static final String DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY = 
     "SELECT authorities"; 


@Autowired 
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { 

     auth.jdbcAuthentication().dataSource(getDataSourceFromJndi()) 
       .usersByUsernameQuery(DEF_USERS_BY_USERNAME_QUERY). 
       authoritiesByUsernameQuery(DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY). 
       groupAuthoritiesByUsername(GROUPS_BY_USERNAME_QUERY); 


    } 
     private DataSource getDataSourceFromJndi() { 
     try { 


      DataSource dataSource = (DataSource) new InitialContext().lookup("DS"); 
      return dataSource; 

     } catch (Exception e) { 
      e.printStackTrace(); 
      return null; 
     } 
    } 

     @Bean 
    @Override 
    public AuthenticationManager authenticationManagerBean() throws Exception { 
     return super.authenticationManagerBean(); 
    } 

    @Override 
    protected void configure(AuthenticationManagerBuilder auth) throws Exception { 
      auth.jdbcAuthentication().dataSource(getDataSourceFromJndi()) 
       .usersByUsernameQuery(DEF_USERS_BY_USERNAME_QUERY). 
       authoritiesByUsernameQuery(DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY). 
       groupAuthoritiesByUsername(GROUPS_BY_USERNAME_QUERY); 

    } 


     @Override 
    protected void configure(HttpSecurity http) throws Exception { 


       // The http.formLogin().defaultSuccessUrl("/path/") method is required when using stateless Spring Security 
     // because the session cannot be used to redirect to the page that was requested while signed out. Unfortunately 
     // using this configuration method will cause our custom success handler (below) to be overridden with the 
     // default success handler. So to replicate the defaultSuccessUrl("/path/") configuration we will instead 
     // correctly configure and delegate to the default success handler. 
     final SimpleUrlAuthenticationSuccessHandler delegate = new SimpleUrlAuthenticationSuccessHandler(); 
     delegate.setDefaultTargetUrl("/api/"); 
      // Make Spring Security stateless. This means no session will be created by Spring Security, nor will it use any 
     // previously existing session. 
     http.sessionManagement().sessionCreationPolicy(STATELESS); 
     // Disable the CSRF prevention because it requires the session, which of course is not available in a 
     // stateless application. It also greatly complicates the requirements for the sign in POST request. 
     http.csrf().disable(); 
     // Viewing any page requires authentication. 
     http.authorizeRequests().anyRequest().authenticated(); 
     http 
      .formLogin().loginPage("http://localhost/web/ui/#access/signin") 
      .permitAll() 
      // Override the sign in success handler with our stateless implementation. This will update the response 
      // with any headers and cookies that are required for subsequent authenticated requests. 
      .successHandler(new NStatelessAuthenticationSuccessHandler(authenticationBinder, delegate)); 
     http.logout().logoutUrl("http://localhost/web/ui/#access/signin").logoutSuccessUrl("http://localhost/web/ui/#access/signin"); 
     // Add our stateless authentication filter before the default sign in filter. The default sign in filter is 
     // still used for the initial sign in, but if a user is authenticated we need to acknowledge this before it is 
     // reached. 
     http.addFilterBefore(
      new StatelessAuthenticationFilter(authenticationBinder, securityContextHolder), 
      UsernamePasswordAuthenticationFilter.class 
     ); 

} 

} 

而且我有兩種類型的authenticationBinder,即TokenBased和UserNameBased。

TokenBased:

@Component 
public class NXAuthTokenHttpServletRequestBinder implements NHttpServletRequestBinder<String> { 

    private static final String X_AUTH_TOKEN = "X-AUTH-TOKEN"; 
    private final NTokenFactory tokenFactory; 

    @Autowired 
    public NXAuthTokenHttpServletRequestBinder(NTokenFactory tokenFactory) { 
     this.tokenFactory = tokenFactory; 
    } 

    @Override 
    public void add(HttpServletResponse response, String username) { 

     final String token = tokenFactory.create(username); 

     response.addHeader(X_AUTH_TOKEN, token); 
     response.addCookie(new Cookie(X_AUTH_TOKEN, token)); 
    } 

    @Override 
    public String retrieve(HttpServletRequest request) { 

     final String cookieToken = findToken(request); 

     if (cookieToken != null) { 
      return tokenFactory.parseUsername(cookieToken); 
     } 

     return null; 
    } 

    private static String findToken(HttpServletRequest request) { 
     Enumeration<String> it = request.getHeaderNames(); 
     while(it.hasMoreElements()){ 
      System.out.println(it.nextElement()); 
     } 
     final String headerToken = request.getHeader(X_AUTH_TOKEN); 

     if (headerToken != null) { 
      return headerToken; 
     } 

     final Cookie[] cookies = request.getCookies(); 

     if (cookies != null) { 
      for (Cookie cookie : cookies) { 
       if (X_AUTH_TOKEN.equals(cookie.getName())) { 
        return cookie.getValue(); 
       } 
      } 
     } 

     return null; 
    } 
} 

基於用戶:

@Component 
@Primary 
public class NUserAuthenticationFactory implements NHttpServletRequestBinder<Authentication> { 

    private final NHttpServletRequestBinder<String> httpServletRequestBinder; 

    @Autowired 

    public NUserAuthenticationFactory(NHttpServletRequestBinder<String> httpServletRequestBinder) { 
     this.httpServletRequestBinder = httpServletRequestBinder; 
    } 

    @Override 
    public void add(HttpServletResponse response, Authentication authentication) { 
     httpServletRequestBinder.add(response, authentication.getName()); 
    } 

    @Override 
    public UserAuthentication retrieve(HttpServletRequest request) { 

     final String username = httpServletRequestBinder.retrieve(request); 

     if (username != null) { 

      return new UserAuthentication(new CustomJDBCDaoImpl().loadUserByUsername(username)); 
     } 

     return null; 
    } 
} 

問題 每當我打開我的應用涉及到基於用戶的身份驗證,然後嘗試從令牌,而不是獲取用戶名從數據庫驗證它。但是到那個時候沒有任何令牌,因爲這是我從UI製作的第一個發佈請求。它會將我重定向到相同的登錄頁面。

日誌:

精細:/在額外的過濾器鏈12的第1位;射擊 過濾器:'WebAsyncManagerIntegrationFilter'罰款:/在位置2的 12在額外的過濾鏈中;點火過濾器: 'SecurityContextPersistenceFilter'罰款:/位於12的第3位 附加過濾器鏈;發射過濾器:'HeaderWriterFilter'罰款:
不注入HSTS頭,因爲它與requestMatcher org.springframework.security.web.header.writers不匹配。HstsHeaderWriter $ SecureRequestMatcher @ a837508 Fine:/位於12的第4位,位於額外的過濾器鏈中;射擊 過濾器:'註銷過濾器'罰款:檢查請求匹配:'/'; 針對'http://localhost/web/ui/#access/signin'罰款:/位置 5 of 12 in additional filter chain;燃燒過濾器: 'StatelessAuthenticationFilter'罰款:/位於12的第6位 額外的過濾器鏈;發射過濾器: 'UsernamePasswordAuthenticationFilter'罰款:請求'GET /'不與 匹配'POST http://localhost/web/ui/#access/signin罰款:/在 位置7的12在另外的過濾器鏈中;點火過濾器: 'RequestCacheAwareFilter'罰款:/位於第12位的另外 過濾器鏈;發射濾波器:'SecurityContextHolderAwareRequestFilter' 精細:/位於附加濾波器鏈12的位置9;射擊 過濾器:'AnonymousAuthenticationFilter'罰款:已填充 SecurityContextHolder with anonymous token: 'org.sprin[email protected]9055e4a6: Principal:anonymousUser;證書:[PROTECTED];已認證: true;詳細信息: org.sprin[email protected]957e: RemoteIpAddress:127.0.0.1; SessionId:null;授予的權限: ROLE_ANONYMOUS'好:/在第12位的第10位附加過濾器 鏈;射擊過濾器:'SessionManagementFilter'罰款:請求 會話ID 3e2c15a2a427bf47e51496d2a186無效。精細:/在 位置11的12在另外的過濾器鏈中;燃燒過濾器: 'ExceptionTranslationFilter'罰款:/位於12的12位 附加過濾鏈; 「FilterSecurityInterceptor」 Fine:Secure object:FilterInvocation:URL:/;屬性: [已驗證]正常:以前已通過身份驗證: org.sprin[email protected]9055e4a6: 負責人:anonymousUser;證書:[PROTECTED];已認證: true;詳細信息: org.sprin[email protected]957e: RemoteIpAddress:127.0.0.1; SessionId:null;授予權限: ROLE_ANONYMOUS罰款:選民: org.sp[email protected]2ac71565, 返回:-1罰款:訪問被拒絕(用戶是匿名的);重定向 到身份驗證入口點 org.springframework.security.access.AccessDeniedException:訪問被拒絕

回答

2

步驟明智的您的問題能夠得到解決..

  1. 使用Spring Security UsernamePasswordAuthenticationFilter到通過用戶名和密碼對用戶進行身份驗證並生成唯一的令牌。
  2. 編寫另一個自定義安全過濾器和AuthenticationProvider實現來驗證用戶的連續請求。下面

    http.addFilterBefore(CustomTokenBasedAuthenticationFilter,UsernamePasswordAuthenticationFilter給出befor UsernamePasswordAuthenticationFilter

  3. 將自定義的安全過濾器。類);

  4. AuthenticationManager

  5. 並且那它註冊AuthenticationProvider實現!

注: -一個更好的辦法,以確保您的REST API是使用一些標準協議,如oauth1a,OAuth2.0的等Spring提供了一個新穎的實施oauth1a和OAuth2.0的協議。