2013-04-19 28 views
7

Tôi có trang đăng nhập nơi người dùng cần đặt thông tin dưới đây số VIN, email, mã zip và mã truy cập mà họ sẽ nhận được từ ứng dụng khác.Cách triển khai bảo mật Mùa xuân khi trang đăng nhập có nhiều trường ngoài tên người dùng và mật khẩu?

Vì vậy, để xác thực người dùng, tôi cần tất cả thông tin trong lớp tùy chỉnh UserDetailsService và sau đó sẽ được gọi là thủ tục để xác thực người dùng.

Nhưng tôi thấy rằng khi tôi thực hiện UserDetailsService như dưới đây

@Component 
public class LoginService implements UserDetailsService { 
@Autowired 
LoginStoredProcedureDao loginStoredProcedureDao; 

public Map<String, Object> verifyLogin(LoginDetails details) { 
    return loginStoredProcedureDao.verifyLogin(details); 

} 
@Override 
public UserDetails loadUserByUsername(String username) 
     throws UsernameNotFoundException { 
    // TODO Auto-generated method stub 
     //verifyLogin(); 
    return null; 
} 

} 

Các loginDetails Object là như dưới đây

public class LoginDetails { 
String vin; 
String email; 
String zipcode; 
String accessCode; 
} 

Trong tình hình trên làm thế nào để sử dụng bảo mật mùa xuân. Ở đây, người dùng cần cung cấp tất cả thông tin để xác thực anh ta.

Trả lời

6

Trước hết, tôi sẽ giải quyết vấn đề của bạn theo cách khác. Tôi sẽ thực hiện xác thực nhiều bước. Đầu tiên sẽ là tên đăng nhập/mật khẩu người dùng truyền thống, sử dụng mô hình mặc định của bảo mật mùa xuân. Bước thứ hai là hiển thị một biểu mẫu khác sẽ được người dùng điền để cung cấp các chi tiết bổ sung cho xác thực, mà ứng dụng của bạn muốn thực thi.

Bất kể, nếu bạn muốn tiếp tục tùy chỉnh mô hình bảo mật mùa xuân để hỏi thêm chi tiết về đăng nhập trong một bước. Làm theo các bước tham chiếu trong câu trả lời trước đó từ @Petr. Và sau đó truy cập các thuộc tính phiên trong lớp UserDetailsService của bạn, sử dụng lớp http://static.springsource.org/spring/docs/2.0.8/api/org/springframework/web/context/request/RequestContextHolder.html do Spring cung cấp.

Bạn có thể truy cập vào currentRequestAttributes(), trả về đối tượng RequestAttributes. Bạn có thể truy vấn đối tượng RequestAttributes để lấy thuộc tính mong muốn từ phạm vi mong muốn.

Lưu ý: Đây là phương pháp tĩnh, có nghĩa là nó sẽ không thân thiện với thử nghiệm đơn vị.

Bạn cũng có thể RequestAttributes nhìn xuống để ServletRequestAttributes nếu bạn muốn nhận được quyền truy cập vào các tiềm ẩn HttpServletRequest

Hope this helps.

2

Here là câu trả lời của bạn, bạn cần triển khai bộ lọc của riêng mình và ghi đè bộ lọc mặc định để thêm thông số vào biểu mẫu đăng nhập.

+3

theo câu trả lời tôi sẽ thêm một bộ lọc và lưu trữ dữ liệu bổ sung trong phiên, nhưng làm thế nào tôi sẽ nhận được đối tượng phiên hoặc yêu cầu đối tượng trong lớp UserDetailsService này không được đề cập trong câu trả lời, xin vui lòng giúp tôi – Krushna

9

Nó không phải là độ linh hoạt của UserDetailsService để xác thực mã thông báo Authentication. Đây là điều mà AuthenticationProvider thực hiện.

Vì vậy, lần đầu tiên rời khỏi thi lại UserDetailsService trách nhiệm duy nhất của tải tất cả các dữ liệu của người sử dụng từ cơ sở dữ liệu bằng cách login:

@Component 
public class UserDetailsServiceImpl implements UserDetailsService { 

    private final UserRepository userRepository; 

    @Autowired 
    public UserDetailsServiceImpl(UserRepository userRepository) { 
     this.userRepository = userRepository; 
    } 

    @Override 
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 
     User user = null; 
     try { 
      user = userRepository.findByUsername(username); 
     } catch (NotFoundException e) { 
      throw new UsernameNotFoundException(String.format("No user found for username %s!", username); 
     } 
     retrun new UserDetailsImpl(user); 
    } 
} 

Thần để đánh chặn các thông số bổ sung từ một hình thức đăng nhập mà bạn cần phải thực hiện AuthenticationDetailsSource. Nó có thể là một ý tưởng tốt để mở rộng WebAuthenticationDetails, nhưng bạn có thể có bất kỳ đối tượng nào được trả về bởi AuthenticationDetailsSource.

@Component 
public class WebAuthenticationDetailsSourceImpl implements AuthenticationDetailsSource<HttpServletRequest, MyWebAuthenticationDetails> { 

    @Override 
    public MyWebAuthenticationDetails buildDetails(HttpServletRequest context) { 
     // the constructor of MyWebAuthenticationDetails can retrieve 
     // all extra parameters given on a login form from the request 
     // MyWebAuthenticationDetails is your LoginDetails class 
     return new MyWebAuthenticationDetails(context); 
    } 
} 

Và để thực hiện xác nhận thực hiện của riêng bạn AuthenticationProvider bởi một trong hai thực hiện giao diện bản thân hoặc gia hạn AbstractUserDetailsAuthenticationProvider hoặc DaoAuthenticationProvider:

@Component 
public class UserDetailsAuthenticationProviderImpl extends AbstractUserDetailsAuthenticationProvider { 

    @Override 
    public Authentication authenticate(Authentication authentication) throws AuthenticationException { 
     MyWebAuthenticationDetails detais = (MyWebAuthenticationDetails) authentication.getDetails(); 
     // verify the authentication details here !!! 
     // and return proper authentication token (see DaoAuthenticationProvider for example) 
    } 
} 

hơn bạn chỉ cần phải vượt qua hiện thực của bạn để AuthenticationManagerUsernamePasswordAuthenticationFilter.

<util:list id="authenticationProviders"> 
    <ref bean="userDetailsAuthenticationProviderImpl" /> 
</util:list> 

<!-- 
    This bean MUST have this exact ID to be the default authenticationManager! 
    This is required prior Spring 3.1, as authentication-manager-ref is not 
    present in sec:http element before! 
--> 
<bean id="org.springframework.security.authenticationManager" 
    name="authenticationManager" 
    class="org.springframework.security.authentication.ProviderManager" 
    c:providers-ref="authenticationProviders" /> 

<bean id="usernamePasswordAuthenticationFilter" 
    class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter" 
    p:authenticationManager-ref="authenticationManager" 
    p:authenticationDetailsSource-ref="webAuthenticationDetailsSourceImpl" /> 

<sec:http authentication-manager-ref="authenticationManager"> 
    <sec:custom-filter position="FORM_LOGIN_FILTER" ref="usernamePasswordAuthenticationFilter" /> 
</sec:http> 

Hy vọng điều này sẽ hữu ích!

P.S. Hãy xem xét việc xây dựng tiêm trên trường tiêm! Nó dễ kiểm tra hơn và cho biết hợp đồng của lớp tốt hơn. Xem this discussion.

+0

Điều này dường như chính xác những gì tôi cần. Thử nghiệm ... Trong khi đó, tại phương thức xác thực trên UserDetailsAuthenticationProviderImpl tôi có thể ném một AuthenticationException tùy chỉnh không? Làm thế nào tôi có thể i18n nó? – Marcelo

+1

Đúng, bạn có thể ném bất kỳ lớp con của AuthenticationException. Ngoại lệ i18n là chủ đề về chủ đề và chủ đề riêng biệt. Tôi muốn có một lớp/giao diện cơ sở ngoại lệ chung mang một khóa thông báo và các tham số được cung cấp tại điểm ném ngoại lệ và được giải quyết trong một '@ ExceptionHandler' chuyên dụng để truyền thông điệp được bản địa hóa tới máy khách. – Roadrunner

+0

Với một chút công việc, điều này đã làm việc cho tôi. Ngoài ra vấn đề i18n tôi đã thực hiện bằng cách thiết lập một số cấu hình mới, nhưng đây là một chủ đề khác. Tôi muốn cảm ơn bạn. – Marcelo

0

Cảm ơn. Tôi đã tạo một lớp lọc tùy chỉnh để xác thực người dùng dựa trên ba tham số - tên người dùng, mật khẩu và id tài khoản. Tôi autowired nó như là một bean trong lớp SecurityConfig:

@Bean 
public AccountCredentialsAuthenticationFilter accountCredentialsAuthenticationFilter() 
     throws Exception { 
    AccountCredentialsAuthenticationFilter accountCredentialsAuthenticationFilter = new AccountCredentialsAuthenticationFilter(); 
    accountCredentialsAuthenticationFilter 
      .setAuthenticationManager(authenticationManagerBean()); 
    return accountCredentialsAuthenticationFilter; 
} 

Vì vậy, thay vì chỉ các thông tin username và mật khẩu truyền thống, tôi đã có thể thực hiện xác thực sử dụng ba lĩnh vực (username, mật khẩu và tài khoản id) bằng cách gọi thích hợp phương pháp dịch vụ cần thiết để thẩm định và cơ quan chức năng thiết lập cho người dùng đăng nhập:

public class AccountCredentialsAuthenticationFilter extends UsernamePasswordAuthenticationFilter { 

@Autowired 
private UserService userService; 

@Qualifier("authenticationManager") 
protected AuthenticationManager authenticationManager; 

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

    String account = request.getParameter("account"); 
    final String userName = request.getParameter("userName"); 
    final String password = request.getParameter("password"); 

    boolean isFound = userService.checkLogin(userName, password, account); 

    if (isFound == true) { 
     boolean selectedAccount = false; 
     UserDetails userDetails = userService.loadUserByUsername(userName); 

     User user = (User) userDetails; 
     Set<Account> accounts = user.getAccounts(); 
     String acctSelect = null; 
     // user has multiple accounts 
     for (Account acct : accounts) { 
      acctSelect = acct.getAccountId().toString(); 
      if (acctSelect.equals(account)) { 
       // confirm which account user has logged in with 
       selectedAccount = true; 

       account = acctSelect; 
       request.getSession().setAttribute("account", account); 

       break; 
      } 
     } 

     if (selectedAccount) { 

      Set<? extends GrantedAuthority> authorities = (HashSet<? extends GrantedAuthority>) userDetails 
        .getAuthorities(); 

      UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userName, password, 
        authorities); 

      token.setDetails(new WebAuthenticationDetails(request)); 

      super.setDetails(request, token); 

      Authentication auth = this.getAuthenticationManager().authenticate(token); 
      SecurityContext securityContext = SecurityContextHolder.getContext(); 
      securityContext.setAuthentication(auth); 
      // Create a new session and add the security context. 
      HttpSession session = request.getSession(true); 
      session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext); 

      return auth; 

     } else { 

      SecurityContextHolder.getContext().setAuthentication(null); 
      request.getSession().setAttribute("SPRING_SECURITY_CONTEXT", null); 

      throw new UsernameNotFoundException("Please input correct credentials"); 
     } 

    } else { 

     SecurityContextHolder.getContext().setAuthentication(null); 
     request.getSession().setAttribute("SPRING_SECURITY_CONTEXT", null); 

     throw new UsernameNotFoundException("Please input correct credentials"); 
    } 

} 

tôi overrode phương pháp của lớp UsernamePasswordAuthenticationFilter để chuyển hướng thích hợp sau khi xác thực & ủy quyền sau:

@Override 
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, 
     Authentication authResult) throws IOException, ServletException { 
    RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
    redirectStrategy.sendRedirect(request, response, "/home"); 

} 

@Override 
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, 
     AuthenticationException failed) throws IOException, ServletException { 
    RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 

    redirectStrategy.sendRedirect(request, response, "/login?error=true"); 

} 

Tôi cũng sửa đổi phương pháp cấu hình trong lớp SecurityConfig để thực hiện các bộ lọc tùy chỉnh:

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

    http.addFilterBefore(accountCredentialsAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class) 
    .authorizeRequests()....rest of the code....} 

Đối với chứng thực tùy chỉnh trong Xuân An, phương pháp

@Override 
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response){---- call service methods here ----} 

trong lớp bộ lọc này (AccountCredentialsAuthenticationFilter) làm cho phương thức sau trong lớp điều khiển dự phòng:

@RequestMapping(value = { "/login" }, method = RequestMethod.POST) 

    public String loginPage(@Valid @ModelAttribute("user") User user, BindingResult result, ModelMap model, HttpServletRequest request){---- call ervice methods here ----} 
Các vấn đề liên quan