Wanneer jy Spring Security gebruik, wat is die regte manier om huidige gebruikersnaam te verkry (d.w.s.SecurityContext) inligting in 'n boontjie?

StackOverflow https://stackoverflow.com/questions/248562

Vra

Ek het 'n Spring MVC-webtoepassing wat Spring Security gebruik.Ek wil die gebruikersnaam van die tans aangemelde gebruiker weet.Ek gebruik die kodebrokkie wat hieronder gegee word.Is dit die aanvaarde manier?

Ek hou nie daarvan om 'n oproep na 'n statiese metode binne hierdie kontroleerder te hê nie - dit verslaan die hele doel van Lente, IMHO.Is daar 'n manier om die toepassing op te stel om eerder die huidige SecurityContext, of huidige stawing, te laat inspuit?

  @RequestMapping(method = RequestMethod.GET)
  public ModelAndView showResults(final HttpServletRequest request...) {
    final String currentUser = SecurityContextHolder.getContext().getAuthentication().getName();
    ...
  }
Was dit nuttig?

Oplossing

As jy ' Lente 3 , die maklikste manier is:

 @RequestMapping(method = RequestMethod.GET)   
 public ModelAndView showResults(final HttpServletRequest request, Principal principal) {

     final String currentUser = principal.getName();

 }

Ander wenke

Baie het verander in die lentewêreld sedert hierdie vraag beantwoord is.Spring het vereenvoudig om die huidige gebruiker in 'n beheerder te kry.Vir ander bone het Spring die voorstelle van die skrywer aangeneem en die inspuiting van 'SecurityContextHolder' vereenvoudig.Meer besonderhede is in die kommentaar.


Dit is die oplossing waarmee ek uiteindelik gegaan het.In plaas daarvan om te gebruik SecurityContextHolder in my kontroleerder wil ek iets inspuit wat gebruik SecurityContextHolder onder die enjinkap, maar onttrek daardie enkelton-agtige klas uit my kode.Ek het geen manier gevind om dit te doen anders as om my eie koppelvlak te rol nie, soos so:

public interface SecurityContextFacade {

  SecurityContext getContext();

  void setContext(SecurityContext securityContext);

}

Nou sal my kontroleerder (of wat ook al POJO) so lyk:

public class FooController {

  private final SecurityContextFacade securityContextFacade;

  public FooController(SecurityContextFacade securityContextFacade) {
    this.securityContextFacade = securityContextFacade;
  }

  public void doSomething(){
    SecurityContext context = securityContextFacade.getContext();
    // do something w/ context
  }

}

En omdat die koppelvlak 'n punt van ontkoppeling is, is eenheidstoetsing eenvoudig.In hierdie voorbeeld gebruik ek Mockito:

public class FooControllerTest {

  private FooController controller;
  private SecurityContextFacade mockSecurityContextFacade;
  private SecurityContext mockSecurityContext;

  @Before
  public void setUp() throws Exception {
    mockSecurityContextFacade = mock(SecurityContextFacade.class);
    mockSecurityContext = mock(SecurityContext.class);
    stub(mockSecurityContextFacade.getContext()).toReturn(mockSecurityContext);
    controller = new FooController(mockSecurityContextFacade);
  }

  @Test
  public void testDoSomething() {
    controller.doSomething();
    verify(mockSecurityContextFacade).getContext();
  }

}

Die verstek implementering van die koppelvlak lyk soos volg:

public class SecurityContextHolderFacade implements SecurityContextFacade {

  public SecurityContext getContext() {
    return SecurityContextHolder.getContext();
  }

  public void setContext(SecurityContext securityContext) {
    SecurityContextHolder.setContext(securityContext);
  }

}

En uiteindelik lyk die produksie-lente-konfigurasie soos volg:

<bean id="myController" class="com.foo.FooController">
     ...
  <constructor-arg index="1">
    <bean class="com.foo.SecurityContextHolderFacade">
  </constructor-arg>
</bean>

Dit lyk meer as 'n bietjie dom dat Spring, 'n afhanklikheidsinspuitinghouer van alle dinge, nie 'n manier verskaf het om iets soortgelyks in te spuit nie.ek verstaan SecurityContextHolder is van acegi geërf, maar tog.Die ding is, hulle is so naby – al is dit net SecurityContextHolder het 'n geter gehad om die onderliggende te kry SecurityContextHolderStrategy instansie (wat 'n koppelvlak is), kan jy dit inspuit.Trouens, ek selfs het 'n Jira-uitgawe oopgemaak tot daardie effek.

Een laaste ding - ek het net die antwoord wat ek voorheen hier gehad het, wesenlik verander.Gaan die geskiedenis na as jy nuuskierig is, maar, soos 'n kollega vir my uitgewys het, sou my vorige antwoord nie in 'n multi-draad omgewing werk nie.Die onderliggende SecurityContextHolderStrategy gebruik deur SecurityContextHolder is by verstek 'n voorbeeld van ThreadLocalSecurityContextHolderStrategy, wat stoor SecurityContexts in a ThreadLocal.Daarom is dit nie noodwendig 'n goeie idee om die in te spuit nie SecurityContext direk in 'n boontjie by inisialisering tyd - dit moet dalk van die ThreadLocal elke keer, in 'n multi-draad omgewing, sodat die korrekte een opgespoor word.

Ek stem saam dat dit nie aan die SecurityContext navraag vir die huidige gebruiker stink, blyk dit 'n baie un-lente manier om hierdie probleem te hanteer.

Ek het 'n statiese "helper" klas te gaan met hierdie probleem; dit is vuil in dat dit 'n globale en statiese metode, maar ek het gedink hierdie manier as ons iets wat verband hou met veiligheid te verander, ten minste ek het net om die besonderhede in een plek te verander:

/**
* Returns the domain User object for the currently logged in user, or null
* if no User is logged in.
* 
* @return User object for the currently logged in user, or null if no User
*         is logged in.
*/
public static User getCurrentUser() {

    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal()

    if (principal instanceof MyUserDetails) return ((MyUserDetails) principal).getUser();

    // principal object is either null or represents anonymous user -
    // neither of which our domain User object can represent - so return null
    return null;
}


/**
 * Utility method to determine if the current user is logged in /
 * authenticated.
 * <p>
 * Equivalent of calling:
 * <p>
 * <code>getCurrentUser() != null</code>
 * 
 * @return if user is logged in
 */
public static boolean isLoggedIn() {
    return getCurrentUser() != null;
}

Maak dit net opdaag in jou JSP bladsye, kan jy die lente Security Tag Lib gebruik:

http://static.springsource.org/spring-security /site/docs/3.0.x/reference/taglibs.html

Om enige van die etikette gebruik, moet jy die sekuriteit taglib verklaar in jou JSP het:

<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>

Toe ek in 'n JSP bladsy so iets te doen:

<security:authorize access="isAuthenticated()">
    logged in as <security:authentication property="principal.username" /> 
</security:authorize>

<security:authorize access="! isAuthenticated()">
    not logged in
</security:authorize>

Nota: Soos genoem in die kommentaar deur @ SBerg413, sal jy nodig het om by te voeg

  

gebruik-uitdrukkings = "true"

om die "http" tag in die security.xml config vir hierdie om te werk.

As jy 'Lente Security ver> = 3.2, kan jy die @AuthenticationPrincipal body gebruik:

@RequestMapping(method = RequestMethod.GET)
public ModelAndView showResults(@AuthenticationPrincipal CustomUser currentUser, HttpServletRequest request) {
    String currentUsername = currentUser.getUsername();
    // ...
}

Hier CustomUser is 'n persoonlike voorwerp wat UserDetails wat teruggekeer deur 'n persoonlike UserDetailsService implemente.

Meer inligting kan gevind word in die @ AuthenticationPrincipal hoofstuk van die lente Security verwysing dokumente.

Ek kry geverifieerde gebruiker deur HttpServletRequest.getUserPrincipal ();

Voorbeeld:

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.support.RequestContext;

import foo.Form;

@Controller
@RequestMapping(value="/welcome")
public class IndexController {

    @RequestMapping(method=RequestMethod.GET)
    public String getCreateForm(Model model, HttpServletRequest request) {

        if(request.getUserPrincipal() != null) {
            String loginName = request.getUserPrincipal().getName();
            System.out.println("loginName : " + loginName );
        }

        model.addAttribute("form", new Form());
        return "welcome";
    }
}

In die lente 3+ jy het volgende opsies.

Opsie 1:

@RequestMapping(method = RequestMethod.GET)    
public String currentUserNameByPrincipal(Principal principal) {
    return principal.getName();
}

Opsie 2:

@RequestMapping(method = RequestMethod.GET)
public String currentUserNameByAuthentication(Authentication authentication) {
    return authentication.getName();
}

Opsie 3:

@RequestMapping(method = RequestMethod.GET)    
public String currentUserByHTTPRequest(HttpServletRequest request) {
    return request.getUserPrincipal().getName();

}

Opsie 4: Fancy een: hierdie Check uit vir meer besonderhede

public ModelAndView someRequestHandler(@ActiveUser User activeUser) {
  ...
}

Ja, statika is oor die algemeen sleg - in die algemeen, maar in hierdie geval, die statiese is die mees veilige kode wat jy kan skryf. Sedert die veilige konteks assosieer 'n skoolhoof met die tans draad, sou die mees veilige kode as direk vanaf die draad toegang tot die statiese as moontlik. Wegsteek van die toegang agter 'n wrapper klas wat ingespuit bied 'n aanvaller met meer punte aan te val. Hulle sal nie toegang tot die kode (wat hulle 'n harde tyd te verander as die pot onderteken sou hê) nodig het, het hulle net 'n manier om die opset, wat kan gedoen word tydens looptyd of gly 'n paar XML op die classpath ignoreer nodig. Selfs die gebruik van body inspuiting in die getekende kode sal overridable met eksterne XML wees. Soos XML kon die loop stelsel te spuit met 'n skelm skoolhoof. Dit is waarskynlik die rede waarom die lente is iets so un-lente-agtige in hierdie geval doen.

Ek wil net dit te doen:

request.getRemoteUser();

Vir die laaste Lente MVC app ek geskryf het, het ek nie spuit die SecurityContext houer, maar ek het wel 'n basis kontroles wat ek moes twee nut metodes wat verband hou met hierdie ... isAuthenticated () & getUsername (). Intern hulle die statiese metode noem jy beskryf.

Ten minste dan is dit net een keer te plaas as wat jy nodig het om later refactor.

Jy kan Lente AOP hoek te benader gebruik. Byvoorbeeld, as jy het 'n paar diens, wat nodig het om die huidige skoolhoof weet. Jy kan stel persoonlike body maw @Principal, wat daarop dui dat hierdie Service moet wees skoolhoof afhanklik.

public class SomeService {
    private String principal;
    @Principal
    public setPrincipal(String principal){
        this.principal=principal;
    }
}

Toe ek in jou raad, wat ek dink behoeftes te MethodBeforeAdvice brei, maak seker dat besondere diens het @Principal body en spuit Skoolhoof naam, of sit dit aan 'Anoniem' plaas.

Die enigste probleem is dat selfs na verifikasie met Spring Security, die gebruiker/hoofboontjie nie in die houer bestaan ​​nie, so afhanklikheid-inspuiting sal moeilik wees.Voordat ons Spring Security gebruik het, sou ons 'n sessie-omvang-boontjie skep wat die huidige skoolhoof het, dit in 'n "AuthService" inspuit en dan daardie diens in die meeste van die ander dienste in die toepassing inspuit.So daardie dienste sal eenvoudig authService.getCurrentUser() bel om die voorwerp te kry.As jy 'n plek in jou kode het waar jy 'n verwysing na dieselfde Skoolhoof in die sessie kry, kan jy dit eenvoudig as 'n eiendom op jou sessie-omvang boontjie stel.

Probeer hierdie

  

verifikasie verifikasie =   . SecurityContextHolder.getContext () getAuthentication ();
  String userName = authentication.getName ();

Die beste oplossing as jy die gebruik Lente 3 en die geverifieerde skoolhoof in jou kontroleerder nodig is om so iets te doen:

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;

    @Controller
    public class KnoteController {
        @RequestMapping(method = RequestMethod.GET)
        public java.lang.String list(Model uiModel, UsernamePasswordAuthenticationToken authToken) {

            if (authToken instanceof UsernamePasswordAuthenticationToken) {
                user = (User) authToken.getPrincipal();
            }
            ...

    }

Ek gebruik die @AuthenticationPrincipal body in @Controller klasse sowel as in @ControllerAdvicer geannoteerde kinders. Ex:.

@ControllerAdvice
public class ControllerAdvicer
{
    private static final Logger LOGGER = LoggerFactory.getLogger(ControllerAdvicer.class);


    @ModelAttribute("userActive")
    public UserActive currentUser(@AuthenticationPrincipal UserActive currentUser)
    {
        return currentUser;
    }
}

Waar UserActive is die klas ek gebruik vir aangemeld gebruikers dienste, en strek vanaf org.springframework.security.core.userdetails.User. Iets soos:

public class UserActive extends org.springframework.security.core.userdetails.User
{

    private final User user;

    public UserActive(User user)
    {
        super(user.getUsername(), user.getPasswordHash(), user.getGrantedAuthorities());
        this.user = user;
    }

     //More functions
}

Baie maklik.

Principal definieer as 'n afhanklikheid in jou kontroleerder metode en lente sal die huidige geverifieerde gebruiker spuit in jou metode by aanroeping.

Ek wil my manier van die ondersteuning van gebruiker besonderhede op freemarker bladsy te deel. Alles is baie eenvoudig en perfek werk!

Jy moet net verifikasie rerequest op default-target-url plaas (bladsy na vorm-login) Dit is my controler metode vir die bladsy:

@RequestMapping(value = "/monitoring", method = RequestMethod.GET)
public ModelAndView getMonitoringPage(Model model, final HttpServletRequest request) {
    showRequestLog("monitoring");


    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    String userName = authentication.getName();
    //create a new session
    HttpSession session = request.getSession(true);
    session.setAttribute("username", userName);

    return new ModelAndView(catalogPath + "monitoring");
}

En dit is my ftl kode:

<@security.authorize ifAnyGranted="ROLE_ADMIN, ROLE_USER">
<p style="padding-right: 20px;">Logged in as ${username!"Anonymous" }</p>
</@security.authorize> 

En dit is dit, sal gebruikersnaam verskyn op elke bladsy na magtiging.

Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top