2015-01-18 17 views
7

Gần đây tôi bắt đầu triển khai hệ thống bảo mật dựa trên mã thông báo với angularjs và mvc mùa xuân. Ý tưởng là như sau: 1. Truy cập/người dùng/xác thực để nhận mã bảo mật và lưu mã thông báo vào bộ nhớ cục bộ 2. Đối với mỗi yêu cầu được khách hàng angularJS gửi, hãy sử dụng trình chặn để chèn tiêu đề X-Auth-Token theo yêu cầu.401 Không được phép do CORS khi gửi yêu cầu nhận mã thông báo người dùng

Trong back-end mùa xuân của tôi, tôi đã triển khai một AuthenticationTokenProcessingFilter và một CustomAuthenticationEntryPoint. Đầu tiên để trích xuất mã thông báo từ tiêu đề và kiểm tra xem mã đó có hợp lệ không và mã thứ hai trả lại trạng thái trái phép 401 khi yêu cầu không được xác thực.

Hãy tìm một số chi tiết về mã back-end của tôi

AuthenticationController.java

@RestController 
@RequestMapping(value="user") 
public class AuthenticationController { 

    @RequestMapping(value="authenticate", method = RequestMethod.POST) 
    public ResponseEntity<?> login(@RequestParam("email") String email, 
     @RequestParam("password") String password) { 
      //Check if user is valid and return token 
     } 
} 

SecurityConfig.java

public class SecurityConfig extends WebSecurityConfigurerAdapter { 

@Autowired 
UsersRepository usersRepo; 

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

@Override 
protected void configure(HttpSecurity http) throws Exception { 
    http 
     .addFilterBefore(
        new AuthenticationTokenProcessingFilter(usersRepo), 
        UsernamePasswordAuthenticationFilter.class) 
     .addFilterBefore(this.corsFilter(), UsernamePasswordAuthenticationFilter.class) 
      .sessionManagement() 
      .sessionCreationPolicy(SessionCreationPolicy.STATELESS) 
     .and() 
      .csrf().disable().exceptionHandling() 
     .and() 
      .httpBasic() 
      .authenticationEntryPoint(new CustomAuthenticationEntryPoint()) 
     .and() 
      .authorizeRequests() 
      .antMatchers(HttpMethod.POST, "/user/authenticate").permitAll() 
      .antMatchers("/**").authenticated() 
      .anyRequest().authenticated(); 
} 

@Bean 
public CORSFilter corsFilter() { 
    return new CORSFilter(); 
} 
} 

CORSFilter.java

public class CORSFilter implements Filter { 

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) 
     throws IOException, ServletException { 
    HttpServletResponse response = (HttpServletResponse) res; 
    response.setHeader("Access-Control-Allow-Origin", "*"); 
    response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT"); 
    response.setHeader("Access-Control-Max-Age", "3600"); 
    response.setHeader("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With, Origin, X-Auth-Token"); 
    response.addHeader("Access-Control-Expose-Headers", "X-Auth-Token"); 
    chain.doFilter(req, res); 
} 
} 

Bây giờ tôi đang sử dụng mã angularjs sau đây để truy vấn điểm cuối/người dùng/xác thực không phải là phía sau tường lửa

return $http.post(baseUrl + 'user/authenticate', 'email='+username+'&password='+password, 
     { 
     headers : { 
      'content-type' : 'application/x-www-form-urlencoded' 
     } 
     } 
); 

Khi tôi sử dụng mã trên, mọi thứ hoạt động. Tuy nhiên Nếu tôi loại bỏ tham số header từ yêu cầu, máy khách angularjs của tôi gửi một yêu cầu OPTION (chứ không phải là một yêu cầu POST - tôi tưởng tượng điều này liên quan đến bộ lọc CORS của tôi) và kết thúc của tôi gửi một phản hồi 401 trái phép.

Bạn có thể vui lòng cho tôi biết thêm một số chi tiết tại sao điều này xảy ra không?

Cảm ơn bạn trước!

+0

Tôi đã thấy câu hỏi liên quan http://stackoverflow.com/questions/24926226/best-practices-for-handling-cors-between-spring-and-angular-js và http://stackoverflow.com/questions này/12111936/angularjs-perform-an-options-http-request-for-a-nguồn gốc chéo – allenhwkim

Trả lời

11

Tôi nghĩ rằng tôi đã có một vấn đề tương tự (giống nhau?) Và tôi đã tinh chỉnh chuỗi bộ lọc của mình theo số WebSecurityConfigurerAdapter::configure(HttpSecurity http) một chút để giải quyết.

Tôi nghĩ điều xảy ra trong trường hợp của bạn là trình duyệt gửi yêu cầu OPTIONS để tìm hiểu xem CORS có được phép hay không bởi máy chủ của bạn.Yêu cầu OPTIONS được xử lý trong chuỗi bộ lọc của bạn và cuối cùng sẽ phù hợp

.antMatchers("/**").authenticated() 

Bây giờ bạn có thể sẽ kết thúc trong CustomAuthenticationEntryPoint của bạn() và trả về một 401.

Bạn có thể thêm:

.antMatchers(HttpMethod.OPTIONS, "/user/authenticate/").permitAll() 

Tuy nhiên, tôi nghĩ bạn vẫn sẽ gặp sự cố sau đó. Khi khách hàng muốn truy cập các tài nguyên khác, mã thông báo phải được gửi trong tiêu đề. Nhiều khả năng điều này sẽ dẫn đến một yêu cầu OPTIONS khác không chứa mã thông báo của bạn và do đó bạn cuối cùng sẽ kết thúc với cùng một vấn đề.

Vì vậy, những gì tôi đã làm là cho phép rõ ràng tất cả yêu cầu OPTIONS mà không kiểm tra xác thực.

.antMatchers(HttpMethod.OPTIONS, "/**").permitAll() 

Bây giờ bạn bỏ qua bộ lọc bảo mật của bạn và bạn sẽ không trở lại một 401. Tôi nghĩ rằng điều này không ảnh hưởng đến sự an toàn của các ứng dụng miễn là bạn sẽ không xử lý OPTIONS yêu cầu của mình trong một bộ điều khiển và cho phép ai đó truy cập dữ liệu quan trọng.

+0

nơi bạn đã đặt '' '.antMatchers (HttpMethod.OPTIONS,"/** "). AllowAll()' ''? –

+0

Nó phải đi vào SecurityConfig của mùa xuân nhìn ví dụ tại blogpost này: http://tux2323.blogspot.com/2013/07/spring-security-java-configuration.html Có rất nhiều ví dụ có sẵn trực tuyến. – sisu

3

OPTIONS yêu cầu là một cái gọi là preflight yêu cầu:

Để bảo vệ tài nguyên đối với các yêu cầu cross-nguồn gốc mà không có thể có nguồn gốc từ đại lý người dùng nhất định trước khi đặc tả này tồn tại một yêu cầu preflight được thực hiện để đảm bảo rằng các tài nguyên nhận thức được đặc điểm này.

Về cơ bản, nó có nghĩa là một yêu cầu OPTIONS được ban hành bất cứ khi nào tài nguyên (backend server) đã không cung cấp OriginAccess-Control-* trong phản ứng (cho khách hàng). Bạn sẽ cần kích hoạt CORS ngay khi chương trình phụ trợ và ứng dụng khách của bạn (webapp) được đặt trong các miền khác nhau, ví dụ: phụ trợ có sẵn theo domainA và khách hàng của bạn có sẵn theo domainB. Ngay cả domainA:80domain:8080 được coi là các miền khác nhau.

Các vấn đề liên quan