簡體   English   中英

當用戶名和密碼除了具有更多字段的登錄頁面時如何實現Spring安全性?

[英]How implement Spring security when login page having more field apart from user name and password?

我有一個登錄頁面,用戶需要在其中輸入以下信息VIN號碼,電子郵件,郵政編碼和accessCode,他們將從不同的應用程序中獲取。

因此,為了驗證用戶,我需要自定義UserDetailsService類中的所有信息,然后調用過程來驗證用戶。

但是當我實現UserDetailsService時,我看到了如下所示

@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;
}

}

loginDetails對象如下所示

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

在上述情況下如何使用彈簧安全。 在這里,用戶需要提供所有信息以驗證他自己。

UserDetailsService的響應性不是驗證Authentication令牌。 這是AuthenticationProvider功能。

因此,首先讓您的UserDetailsService實現負責通過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);
    }
}

要從登錄表單中截取其他參數,您需要實現AuthenticationDetailsSource 擴展WebAuthenticationDetails可能是一個好主意,但您可以只使用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);
    }
}

要通過實現接口本身或擴展AbstractUserDetailsAuthenticationProviderDaoAuthenticationProvider來驗證實現您自己的AuthenticationProvider

@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)
    }
}

您只需要將您的實現傳遞給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>

希望這可以幫助!

PS考慮構造函數注入場注入! 它更可測試並更好地說明了課程的合同。 這個討論

首先,我會以不同的方式解決您的問題。 我會做一個多步驗證。 第一個是傳統的用戶名/密碼登錄,使用spring security的默認模型。 第二步是顯示另一個表單,該表單必須由用戶填寫,以提供應用程序想要強制執行的身份驗證的其他詳細信息。

無論如何,如果您想繼續自定義彈簧安全模型,只需一步即可了解有關登錄的更多詳細信息。 按照@Petr上一個答案中的步驟參考。 然后要訪問UserDetailsS​​ervice類中的會話屬性,請使用Spring提供的http://static.springsource.org/spring/docs/2.0.8/api/org/springframework/web/context/request/RequestContextHolder.html類。

您可以訪問currentRequestAttributes() ,它返回一個RequestAttributes對象。 您可以查詢RequestAttributes對象以從所需范圍獲取所需的屬性。

注意:這是一種靜態方法,這意味着它不會對單元測試友好。

如果要訪問底層的HttpServletRequest還可以將RequestAttributes轉發給ServletRequestAttributes

希望這可以幫助。

是您的答案,您需要實現自己的過濾器並覆蓋默認過濾器,以便將參數添加到登錄表單。

謝謝。 我創建了一個自定義過濾器類,用於根據三個參數(用戶名,密碼和帳戶ID)對用戶進行身份驗證。 我在SecurityConfig類中將它作為bean自動啟動:

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

因此,通過調用身份驗證所需的適當服務方法並為登錄用戶設置權限,我可以使用三個字段(用戶名,密碼和帳戶ID)執行身份驗證,而不僅僅是傳統的用戶名和密碼字段:

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");
    }

}

在認證和授權之后,我覆蓋了UsernamePasswordAuthenticationFilter類的以下方法以進行適當的重定向:

@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");

}

我還修改了SecurityConfig類中的configure方法來執行自定義過濾器:

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

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

對於Spring Security中的自定義身份驗證,該方法

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

在此過濾器類(AccountCredentialsAuthenticationFilter)中,控制器類中的以下方法是多余的:

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

   public String loginPage(@Valid @ModelAttribute("user") User user, BindingResult result, ModelMap model, HttpServletRequest request){---- call ervice methods here ----}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM