Spring Security / SpringMVC'de kimliği doğrulanmış bir kullanıcı manuel olarak nasıl ayarlanır


107

Yeni bir kullanıcı bir 'Yeni hesap' formu gönderdikten sonra, sonraki sayfada oturum açmalarına gerek kalmaması için bu kullanıcıyı manuel olarak oturum açmak istiyorum.

Yaylı güvenlik önleyiciden geçen normal form giriş sayfası gayet iyi çalışıyor.

Yeni hesap formu denetleyicisinde bir UsernamePasswordAuthenticationToken oluşturuyorum ve bunu SecurityContext'te manuel olarak ayarlıyorum:

SecurityContextHolder.getContext().setAuthentication(authentication);

Aynı sayfada daha sonra kullanıcının şu şekilde oturum açtığını kontrol ediyorum:

SecurityContextHolder.getContext().getAuthentication().getAuthorities();

Bu, kimlik doğrulamasında daha önce belirlediğim yetkileri döndürür. Herşey iyi.

Ancak yüklediğim sonraki sayfada aynı kod çağrıldığında, kimlik doğrulama belirteci yalnızca UserAnonymous'dur.

Önceki talepte belirlediğim kimlik doğrulamasını neden saklamadığından emin değilim. Düşüncesi olan var mı?

  • Oturum kimliğinin doğru ayarlanmaması ile bir ilgisi olabilir mi?
  • Bir şekilde kimlik doğrulamamın üzerine yazan bir şey var mı?
  • Belki de kimlik doğrulamayı kaydetmek için başka bir adıma ihtiyacım var?
  • Veya bir şekilde tek bir istek yerine tüm oturum boyunca kimlik doğrulamasını bildirmek için yapmam gereken bir şey var mı?

Burada neler olduğunu görmeme yardımcı olabilecek bazı düşünceler arıyorum.



2
Onlar yapmak söylerseniz Okuyucular, bu soruya cevap sakının: SecurityContextHolder.getContext().setAuthentication(authentication). Çalışır ve yaygındır, ancak bunu yaparsanız karşılaşacağınız ciddi işlevsellik eksiklikleri vardır. Daha fazla bilgi için soruma ve cevaba bakın: stackoverflow.com/questions/47233187/…
keçi

Yanıtlar:


62

Bir süre önce seninle aynı sorunu yaşadım. Ayrıntıları hatırlayamıyorum, ancak aşağıdaki kod benim için işe yarayan şeyleri sağladı. Bu kod, bir Bahar Web Akışı akışında, dolayısıyla RequestContext ve ExternalContext sınıflarında kullanılır. Ancak sizinle en alakalı kısım doAutoLogin yöntemidir.

public String registerUser(UserRegistrationFormBean userRegistrationFormBean,
                           RequestContext requestContext,
                           ExternalContext externalContext) {

    try {
        Locale userLocale = requestContext.getExternalContext().getLocale();
        this.userService.createNewUser(userRegistrationFormBean, userLocale, Constants.SYSTEM_USER_ID);
        String emailAddress = userRegistrationFormBean.getChooseEmailAddressFormBean().getEmailAddress();
        String password = userRegistrationFormBean.getChoosePasswordFormBean().getPassword();
        doAutoLogin(emailAddress, password, (HttpServletRequest) externalContext.getNativeRequest());
        return "success";

    } catch (EmailAddressNotUniqueException e) {
        MessageResolver messageResolvable 
                = new MessageBuilder().error()
                                      .source(UserRegistrationFormBean.PROPERTYNAME_EMAIL_ADDRESS)
                                      .code("userRegistration.emailAddress.not.unique")
                                      .build();
        requestContext.getMessageContext().addMessage(messageResolvable);
        return "error";
    }

}


private void doAutoLogin(String username, String password, HttpServletRequest request) {

    try {
        // Must be called from request filtered by Spring Security, otherwise SecurityContextHolder is not updated
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
        token.setDetails(new WebAuthenticationDetails(request));
        Authentication authentication = this.authenticationProvider.authenticate(token);
        logger.debug("Logging in with [{}]", authentication.getPrincipal());
        SecurityContextHolder.getContext().setAuthentication(authentication);
    } catch (Exception e) {
        SecurityContextHolder.getContext().setAuthentication(null);
        logger.error("Failure in autoLogin", e);
    }

}

2
Teşekkürler, kod, doğru alanda sorun giderdiğimi bilmeme yardımcı olmak için çok yardımcı oluyor. Görünüşe göre bir silahım var, manuel kimlik doğrulamasından sonra yeni bir oturum kimliği oluşturuyor, ancak eski oturum kimliği hala çerezden tanımlanıyor. Nedenini şimdi anlamalıyım, ama en azından açık bir şekilde yolundayım. Teşekkürler!
David Parks

4
Bu kılavuza uyan herkes, bu ilgili sorunu da görmelidir: stackoverflow.com/questions/4824395/…
David Parks

14
Kimlik doğrulamasını nasıl aldığınızı açıklayabilir misiniz?
Sağlayıcı

1
@ s1moner3d, onu IoC aracılığıyla enjekte ettirebilmeniz gerekir -> \ @Autowired
Hartmut

1
@Configuration public class WebConfig extends WebSecurityConfigurerAdapter { @Bean @Override public AuthenticationManager authenticationProvider() throws Exception { return super.authenticationManagerBean(); } }
slisnychyi

66

Başka tam çözüm bulamadım, bu yüzden benimkini göndereceğimi düşündüm. Bu biraz hack olabilir, ancak sorunu yukarıdaki soruna çözdü:

public void login(HttpServletRequest request, String userName, String password)
{

    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(userName, password);

    // Authenticate the user
    Authentication authentication = authenticationManager.authenticate(authRequest);
    SecurityContext securityContext = SecurityContextHolder.getContext();
    securityContext.setAuthentication(authentication);

    // Create a new session and add the security context.
    HttpSession session = request.getSession(true);
    session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
}

7
+1 - Bu bana yardımcı oldu! SPRING_SECURITY_CONTEXT güncellemesini kaçırdım. ... Ama bu ne kadar "kirli"?
l3dx

12
nerede alabilirim authenticationManagerdan?
Isaac

2
authenticationManager, sınıfınızda bu @Autowired AuthenticationServiceImpl authenticationManager gibi otomatik olarak kablolanır. Ayrıca xml yapılandırmanızda bir fasulye enjeksiyonu da olmalıdır, böylece Spring ne enjekte edeceğini bilir.

1
AuthenticationServiceImpl uygulaması nerede? Bu sınıfta neler var?
Pra_A

3
Yeni bir oturum oluşturmak neden gerekli? SecurityContext bunu çözmüyor mu?
Vlad Manuel Mureșan

17

Sonunda sorunun kökenini anladım.

Güvenlik bağlamını manuel olarak oluşturduğumda, hiçbir oturum nesnesi oluşturulmadı. Spring Security mekanizması, yalnızca isteğin işlenmesi bittiğinde, oturum nesnesinin boş olduğunu fark eder (istek işlendikten sonra güvenlik içeriğini oturuma kaydetmeye çalıştığında).

Spring Security, isteğin sonunda yeni bir oturum nesnesi ve oturum kimliği oluşturur. Ancak bu yeni oturum kimliği, tarayıcıya yanıt verildikten sonra isteğin sonunda gerçekleştiği için hiçbir zaman tarayıcıya ulaşmaz. Bu, sonraki istek önceki oturum kimliğini içerdiğinde yeni oturum kimliğinin (ve dolayısıyla el ile oturum açan kullanıcımı içeren Güvenlik bağlamının) kaybolmasına neden olur.


4
Dürüst olmak gerekirse bu, her şeyden çok yay güvenliğinde bir tasarım hatası gibi geliyor. Başka dillerde yazılmış, bununla hiçbir problemi olmayan pek çok çerçeve var, ancak Bahar Güvenliği kırılıyor.
chubbsondubs

3
ve çözüm?
s1moner3d

2
ve çözüm nedir?
Thiago

6

Neler olup bittiğini daha iyi anlamak için hata ayıklama günlüğünü açın.

HTTP yanıtlarında döndürülen başlıklara bakmak için tarayıcı tarafında bir hata ayıklayıcı kullanarak oturum tanımlama bilgilerinin ayarlanıp ayarlanmadığını anlayabilirsiniz. (Başka yollar da var.)

Bir olasılık, SpringSecurity'nin güvenli oturum çerezleri ayarlaması ve istenen sonraki sayfanızın "https" URL'si yerine "http" URL'sine sahip olmasıdır. (Tarayıcı, "http" URL'si için güvenli bir çerez göndermez.)


Teşekkürler, bunların hepsi çok yararlı ve alakalı önerilerdi!
David Parks

5

Servlet 2.4'teki yeni filtreleme özelliği temelde filtrelerin yalnızca istek akışında uygulama sunucusu tarafından gerçek istek işlemeden önce ve sonra çalışabileceği kısıtlamasını hafifletir. Bunun yerine, Servlet 2.4 filtreleri artık her dağıtım noktasında istek dağıtıcısı ile etkileşime girebilir. Bu, bir Web kaynağı bir isteği başka bir kaynağa ilettiğinde (örneğin, isteği aynı uygulamadaki bir JSP sayfasına ileten bir sunucu uygulaması), istek hedeflenen kaynak tarafından işlenmeden önce bir filtre çalışabileceği anlamına gelir. Ayrıca, bir Web kaynağının diğer Web kaynaklarından (örneğin, diğer birçok JSP sayfasından çıktıyı içeren bir JSP sayfası) çıktı veya işlevi içermesi durumunda, Servlet 2.4 filtrelerinin, dahil edilen her bir kaynaktan önce ve sonra çalışabileceği anlamına gelir. .

Bu özelliği açmak için ihtiyacınız olan:

web.xml

<filter>   
    <filter-name>springSecurityFilterChain</filter-name>   
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 
</filter>  
<filter-mapping>   
    <filter-name>springSecurityFilterChain</filter-name>   
    <url-pattern>/<strike>*</strike></url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

Kayıt Kontrolör

return "forward:/login?j_username=" + registrationModel.getUserEmail()
        + "&j_password=" + registrationModel.getPassword();

İyi bilgi, ancak kullanıcı adı ve şifreyi url'ye eklemek kötü. 1) kaçış yapılmaz, bu nedenle özel karaktere sahip bir kullanıcı adı veya şifre büyük olasılıkla kırılır veya daha da kötüsü bir güvenlik istismar vektörü olarak kullanılır. 2) url'lerdeki parolalar kötüdür çünkü url'ler genellikle diske kaydedilir ve bu güvenlik açısından oldukça kötüdür - tüm parolalarınız düz metin olarak orada durur.
keçi

1

Bir extjs uygulamasını test etmeye çalışıyordum ve bir testAuthenticationToken'ı başarıyla ayarladıktan sonra bu, bariz bir neden olmadan aniden çalışmayı durdurdu.

Çalışmak için yukarıdaki cevapları alamadım, bu yüzden benim çözümüm test ortamında bu yayı atlamaktı. İlkbaharda şöyle bir dikiş koydum:

public class SpringUserAccessor implements UserAccessor
{
    @Override
    public User getUser()
    {
        SecurityContext context = SecurityContextHolder.getContext();
        Authentication authentication = context.getAuthentication();
        return (User) authentication.getPrincipal();
    }
}

Kullanıcı burada özel bir türdür.

Daha sonra, test kodunun yayı değiştirmesi için bir seçeneğe sahip olan bir sınıfa sarıyorum.

public class CurrentUserAccessor
{
    private static UserAccessor _accessor;

    public CurrentUserAccessor()
    {
        _accessor = new SpringUserAccessor();
    }

    public User getUser()
    {
        return _accessor.getUser();
    }

    public static void UseTestingAccessor(User user)
    {
        _accessor = new TestUserAccessor(user);
    }
}

Test sürümü şuna benzer:

public class TestUserAccessor implements UserAccessor
{
    private static User _user;

    public TestUserAccessor(User user)
    {
        _user = user;
    }

    @Override
    public User getUser()
    {
        return _user;
    }
}

Çağıran kodda hala veritabanından yüklenen uygun bir kullanıcıyı kullanıyorum:

    User user = (User) _userService.loadUserByUsername(username);
    CurrentUserAccessor.UseTestingAccessor(user);

Açıkçası, gerçekten güvenliği kullanmanız gerekiyorsa bu uygun olmayacaktır, ancak test dağıtımı için güvenliksiz bir kurulumla çalışıyorum. Başka birinin benzer bir durumla karşılaşabileceğini düşündüm. Bu, daha önce statik bağımlılıkları alay etmek için kullandığım bir model. Diğer alternatif ise, sarmalayıcı sınıfının durağanlığını koruyabilmenizdir, ancak bunu gerekli olduğu sınıflara CurrentUserAccessor'ı geçirmeniz gerektiğinden kodun bağımlılıkları daha açık olduğu için tercih ediyorum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.