2014-09-11 25 views
13

Hiện tại tôi có một cơ chế xác thực đơn trong ứng dụng của mình là sử dụng LDAP để xác thực và ủy quyền. cấu hình bảo mật của tôi trông như thế nàynhiều cơ chế xác thực trong một ứng dụng sử dụng cấu hình java

@Configuration 
@EnableWebMvcSecurity 
public class SecurityConfig extends WebSecurityConfigurerAdapter { 

@Override 
protected void configure(HttpSecurity http) throws Exception { 
    http 
     .csrf().disable() 
     .authorizeRequests() 
      .anyRequest().fullyAuthenticated() 
      .and() 
      .httpBasic(); 
} 

@Configuration 
protected static class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter { 

    @Value("${ldap-${env}.manager.dn}") 
    private String managerDn; 

    @Value("${ldap-${env}.manager.pass}") 
    private String managerPass; 

    @Value("${ldap-${env}.server.url}") 
    private String url; 

    @Value("${ldap.password.attribute:userPassword}") 
    private String passwordAttr; 

    @Override 
    public void init(AuthenticationManagerBuilder auth) throws Exception { 
     auth.ldapAuthentication().userDnPatterns("uid={0},ou=people").groupSearchBase("ou=groups") 
       .groupSearchFilter("(member={0})").userSearchBase("ou=people").userSearchFilter("(uid={0})") 
       .userDetailsContextMapper(new CustomLdapPersonContextMapper()) 
       // .passwordCompare() 
       // .passwordAttribute(passwordAttr) 
       // .passwordEncoder(new PlaintextPasswordEncoder()) 
       // .and() 
       .contextSource().managerDn(managerDn).managerPassword(managerPass).url(url); 
    } 
} 
} 

Có những tình huống mặc dù nơi người dùng có thể đến với một mã thông báo phiên mà có thể xác thực từ một phiên phục vụ chủ chốt và mã thông báo hợp lệ trả về một tên người dùng mà sau đó có thể được sử dụng để tải authrization thông tin từ LDAP cho người dùng đó. Vì vậy, cơ chế xác thực thứ hai của tôi sẽ xảy ra trước tiên nếu một mã thông báo phiên xuất hiện trong tiêu đề http, nó sẽ thực hiện xác thực mã thông báo và tra cứu ldap và nếu không có mã phiên, nó sẽ chỉ rơi vào cơ chế xác thực hiện tại. Làm thế nào tôi có thể thêm lớp xác thực thứ hai này.

Trả lời

26

Tôi đã dành một chút thời gian gói đầu xung quanh bảo mật mùa xuân khi sử dụng cấu hình java thuần túy. Có một vài bước liên quan đến việc làm việc này. Nó phải là một cái gì đó dọc theo những dòng này. Quá trình cơ bản như sau:

  • Tạo bộ lọc tùy chỉnh để kiểm tra yêu cầu cung cấp thông tin ủy quyền cụ thể

  • Mỗi bộ lọc trả về null (nếu không có uỷ quyền của loại được tìm thấy), hoặc một tùy chỉnh AbstractAuthenticationToken

  • Nếu bộ lọc trả về mã thông báo, mỗi phương thức hỗ trợ (lớp) của AuthenticationProvider sẽ được gọi với mã thông báo đó trả về true | false nếu nó nên thử xác thực

  • tryAuthentication sau đó sẽ được gọi trên AuthenticationProvider hỗ trợ mã thông báo. Ở đây bạn thực hiện bất kỳ cuộc gọi dịch vụ nào để xác thực người dùng. Sau đó bạn có thể ném LoginException hoặc gọi authentication.setAuthenticated (true) và trả lại mã thông báo để xác thực thành công.

Tôi đã sử dụng thiết lập này một lúc để hỗ trợ nhiều phương thức xác thực khác nhau (yêu cầu đã ký tên, tên người dùng/mật khẩu, oauth v.v.) và hoạt động khá tốt.

Bạn cũng có thể chuyển AuthenticationSuccessHandler và AuthenticationFailuersHandler cho các bộ lọc bảo mật tùy chỉnh để cung cấp các chiến lược chuyển hướng tùy chỉnh và xử lý lỗi.

Ngoài ra, hãy đảm bảo thiết lập trình so khớp kiến ​​trong các hàm tạo của bộ lọc để kiểm soát các mẫu url mà bộ lọc cũng áp dụng. Ví dụ, một bộ lọc yêu cầu ldap có lẽ sẽ cần phải được kiểm tra với bất kỳ yêu cầu "/ *" trong khi một bộ lọc tên người dùng/mật khẩu chỉ có thể được kiểm tra trên POST để/đăng nhập hoặc một cái gì đó tương tự.

Ví dụ Code:

1) Tạo tùy chỉnh AuthenticationToken cho từng loại chứng thực bạn muốn hỗ trợ

public class LDAPAuthorizationToken extends AbstractAuthenticationToken { 
    private String token; 

    public LDAPAuthorizationToken(String token) { 
     super(null); 
     this.token = token; 
    } 

    public Object getCredentials() { 
     return token; 
    } 

    public Object getPrincipal() { 
     return null; 
    } 
} 

public class OTPAuthorizationToken extends UsernamePasswordAuthenticationToken { 
    private String otp; 

    public OTPAuthorizationToken(String username, String password, String otp) { 
     super(username, password); 
     this.otp = otp; 
    } 

    public String getOTP() { 
     return otp; 
    } 
} 

2) Tạo bộ lọc an ninh tùy chỉnh cho từng loại

public class LDAPAuthorizationFilter extends AbstractAuthenticationProcessingFilter { 
    @Autowired 
    private UserDetailsService userDetailsService; 

    public LDAPAuthorizationFilter() { 
     super("/*"); // allow any request to contain an authorization header 
    } 

    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException 
    { 

     if (request.getHeader("Authorization") == null) { 
      return null; // no header found, continue on to other security filters 
     } 

     // return a new authentication token to be processed by the authentication provider 
     return new LDAPAuthorizationToken(request.getHeader("Authorization")); 
    } 
} 

public class OTPAuthorizationFilter extends AbstractAuthenticationProcessingFilter { 
    @Autowired 
    private UserDetailsService userDetailsService; 

    public OTPAuthorizationFilter() { 
     super("/otp_login"); 
    } 

    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException 
    { 

     if (request.getParameter("username") == null || request.getParameter("password") == null || request.getParameter("otp") == null) { 
      return null; 
     } 

     // return a new authentication token to be processed by the authentication provider 
     return new OTPAuthorizationToken(request.getParameter("username"), request.getParameter("password"), request.getParameter("otp")); 
    } 
} 

3) Tạo Trình xác thực tùy chỉnh

public class LDAPAuthenticationProvider implements AuthenticationProvider { 

    @Autowired 
    private MyAuthenticationService sampleService; 

    @Override 
    public Authentication authenticate(Authentication authentication) throws AuthenticationException { 
     LDAPAuthorizationToken auth = (LDAPAuthorizationToken)authentication; 

     String username = sampleService.verifyToken(auth.getCredentials()); 
     if (username == null) { 
      throw new LoginException("Invalid Token"); 
     } 

     auth.setAuthenticated(true); 

     return auth; 
    } 

    @Override 
    public boolean supports(Class<?> authentication) { 
     if (authentication.isAssignableFrom(LDAPAuthorizationToken.class)) { 
      return true; 
     } 
     return false; 
    } 
} 

public class OTPAuthenticationProvider implements AuthenticationProvider { 

    @Autowired 
    private MyAuthenticationService sampleService; 

    @Override 
    public Authentication authenticate(Authentication authentication) throws AuthenticationException { 
     OTPAuthorizationToken auth = (OTPAuthorizationToken)authentication; 

     String error = sampleService.loginWithOTP(auth.getPrincipal(), auth.getCredentials(), auth.getOTP()); 
     if (error != null) { 
      throw new LoginException(error); 
     } 

     auth.setAuthenticated(true); 

     return auth; 
    } 

    @Override 
    public boolean supports(Class<?> authentication) { 
     if (authentication.isAssignableFrom(OTPAuthorizationToken.class)) { 
      return true; 
     } 
     return false; 
    } 
} 

4) Cấu hình bảo mật mùa xuân

public class SecurityConfiguration extends WebSecurityConfigurerAdapter { 
    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     // configure filters 
     http.addFilterBefore(new LDAPAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class); 
     http.addFilterBefore(new OTPAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class); 

     // configure authentication providers 
     http.authenticationProvider(new LDAPAuthenticationProvider()); 
     http.authenticationProvider(new OTPAuthenticationProvider()); 

     // disable csrf 
     http.csrf().disable(); 

     // setup security 
     http.authorizeRequests() 
      .anyRequest() 
       .fullyAuthenticated() 
       .and().httpBasic(); 
    } 
} 

Hy vọng rằng sẽ giúp!

+0

nó hoạt động? Khóa tiêu đề "Ủy quyền" là gì? URL auth là gì? Cảm ơn – tayfun

8

Một tùy chọn khác để thêm nhà cung cấp xác thực thứ hai: Chỉ cần chỉ định một nhà cung cấp khác trên AuthenticationManagerBuilder. Vì chú thích @EnableWebSecurity được tự chú thích với EnableGlobalAuthentication, bạn có thể định cấu hình phiên bản toàn cầu là AuthenticationManagerBuilder. (Xem chi tiết javadocs để biết thêm chi tiết.)

Ví dụ: tại đây chúng tôi có nhà cung cấp xác thực LDAP cũng như nhà cung cấp xác thực bộ nhớ (được mã hóa cứng) (đây là điều chúng tôi phát triển để người dùng địa phương thử nghiệm) with):

@Configuration 
    @EnableWebSecurity 
    public class SecurityConfig extends WebSecurityConfigurerAdapter { 

     @Value("${user.role}") 
     private String userRole; // i.e. ROLE_APP_USER 

     @Value("${include.test.users}") 
     private boolean includeTestUsers; 

     @Override 
     protected void configure(HttpSecurity http) throws Exception { 
     http.authorizeRequests() 
      .antMatchers("/**/js/**").permitAll() 
      .antMatchers("/**/images/**").permitAll() 
      .antMatchers("/**/favicon.ico").permitAll() 
      .antMatchers("/**/css/**").permitAll() 
      .antMatchers("/**/fonts/**").permitAll() 
      .antMatchers("/**").hasAnyRole(userRole) 
      .and().formLogin().loginPage("/login").permitAll().and().logout().permitAll(); 

     http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")); 
     } 

     @Autowired 
     public void configureGlobal(AuthenticationManagerBuilder auth, LdapContextSource contextSource) throws Exception { 
     auth.ldapAuthentication() 
      .userSearchBase("OU=Users OU") 
      .userSearchFilter("sAMAccountName={0}") 
      .groupSearchBase("OU=Groups OU") 
      .groupSearchFilter("member={0}") 
      .contextSource(contextSource); 

     if (includeTestUsers) { 
      auth.inMemoryAuthentication().withUser("user").password("u").authorities(userRole); 
     } 
     } 
    } 
+0

Điều này có vẻ như câu trả lời đơn giản và chính xác nhất. –

0

Tôi muốn thêm vào câu trả lời của mclema. Bạn có thể cần phải thêm ghi đè để xác thực thành công và tiếp tục chuỗi bộ lọc hoặc người dùng khác được chuyển hướng đến url mặc định ("/") thay cho URL gốc (ví dụ:/myrest/server/somemethod)

@Override 
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, 
     Authentication authResult) throws IOException, ServletException { 
    SecurityContext context = SecurityContextHolder.createEmptyContext(); 
    context.setAuthentication(authResult); 
    SecurityContextHolder.setContext(context); 
    chain.doFilter(request, response); 
} 
Các vấn đề liên quan