In addition to bearer token authentication, I am also trying to validate user roles/authorities to ensure that they are permitted to access this resource. Here is what I have so far:
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${oauth.enabled}")
boolean oauthEnabled;
#Value("${spring.security.oauth2.resourceserver.opaque.introspection-uri}")
String introspectionUri;
#Value("${spring.security.oauth2.resourceserver.opaque.introspection-client-id}")
String clientId;
#Value("${spring.security.oauth2.resourceserver.opaque.introspection-client-secret}")
String clientSecret;
#Override
public void configure(final HttpSecurity http) throws Exception {
if (oauthEnabled) {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().cors()
.and()
.authorizeRequests(urlRegistry -> urlRegistry
.antMatchers("/api/health").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(resourceServer -> resourceServer
.opaqueToken(opaqueToken -> opaqueToken
.introspectionUri(this.introspectionUri)
.introspectionClientCredentials(this.clientId, this.clientSecret)
)
);
}
#Slf4j
#RestController
#RequestMapping("/api")
#ControllerAdvice
#CrossOrigin(originPatterns = "*")
public class InventoryCountdownController extends BaseController {
#GetMapping("/icd")
//#PreAuthorize("permitAll()")
#PreAuthorize("hasAuthority('SOME_USER_ROLE')")
public ResponseEntity<List<Countdown>> getIcd(#RequestParam(value = "val") String val) {
...
}
The problem that I am running into is that I am getting back "Unexpected error: Access is denied". When I replace the "hasAuthority" annotation with #PreAuthorize("permitAll()"), it seems to work fine. What am I missing?
As per https://github.com/spring-projects/spring-security/wiki/OAuth-2.0-Migration-Guide, I am using org.springframework.boot:spring-boot-starter-oauth2-resource-server to implement my resource server.
The default behaviour is to populate the authorities based on the "scope" attribute that is typically included in the response from the introspection endpoint.
For example, if the introspection endpoint responds with { …, "scope" : "messages"} then the authority list will be ["SCOPE_messages"].
You can customise this using a custom OpaqueTokenIntrospector and exposing it as a bean.
#Bean
public OpaqueTokenIntrospector introspector() {
return new CustomAuthoritiesOpaqueTokenIntrospector();
}
where CustomAuthoritiesOpaqueTokenIntrospector will look similar to this
public class CustomAuthoritiesOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
private OpaqueTokenIntrospector delegate =
new NimbusOpaqueTokenIntrospector("https://idp.example.org/introspect", "client", "secret");
public OAuth2AuthenticatedPrincipal introspect(String token) {
OAuth2AuthenticatedPrincipal principal = this.delegate.introspect(token);
return new DefaultOAuth2AuthenticatedPrincipal(
principal.getName(), principal.getAttributes(), extractAuthorities(principal));
}
private Collection<GrantedAuthority> extractAuthorities(OAuth2AuthenticatedPrincipal principal) {
List<String> scopes = principal.getAttribute(OAuth2IntrospectionClaimNames.SCOPE);
return scopes.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
}
This is all described in the Spring Security reference documentation.
the client et secret should be the credentials of the front application or the backend( resource server). should I store in the resource server th cient id ans secret of all clients that are calling my rest api
Related
My goal is to authenticate an user through an authentication service (like google or github).
I tried to use both, and I don't understand why with github my authentication is handled with my OAuth2UserService, while with google, this is my OidcUserService which is called.
I expect that both call OidcUserService because it's only authentification that I need.
So, why there is such a difference ?
Can you enlighten me about that ?
Do I miss something ?
Some code to illustrate
#Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
#Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2User user = super.loadUser(userRequest);
log.info("OAuth2User loading");
return user;
}
}
#Service
public class CustomOidcUserService extends OidcUserService {
#Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
OidcUser user = super.loadUser(userRequest);
log.info("OidcUser loading");
return user;
}
}
// MyAppSecurityConfig.java
#Configuration
public class MyAppSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
;
}
}
# application.properties
spring.security.oauth2.client.registration.github.client-id=xxxx
spring.security.oauth2.client.registration.github.client-secret=xxx
spring.security.oauth2.client.registration.google.client-id=xxxx
spring.security.oauth2.client.registration.google.client-secret=xxxx
The behaviour you are observing caused by predefined oauth2 configurations in spring-boot:
For common OAuth2 and OpenID providers, including Google, Github,
Facebook, and Okta, we provide a set of provider defaults (google,
github, facebook, and okta, respectively).
If you do not need to customize these providers, you can set the
provider attribute to the one for which you need to infer defaults.
Also, if the key for the client registration matches a default
supported provider, Spring Boot infers that as well.
i.e. spring boot has preconfigured openid-connect for google services and generic oauth2 for github.
The question is a little bit longer than expected. Below is the link to a similar one (3rd post) where I didn't find the answer satisfying.
TL;DR
I am trying to logout using the JAAS Login Module. Here is the brief structure of the project:
LoginService is responsible for instantiating LoginContext when a user wants to log in:
#Service
public class LoginService {
public UserDTO getUserDTOFrom(Credentials credentials) {
try {
LoginContext loginContext = new LoginContext("Login", new JAASCallbackHandler(credentials));
loginContext.login();
// construct UserDTO object.
} catch (LoginException e) {
LOGGER.error("Login Exception: {}", e.getMessage());
// construct UserDTO object.
}
// return UserDTO object.
}
The LoginController calls the method:
#RestController
#RequestMapping("/login")
public class LoginController {
private final LoginService loginService;
#Autowired
public LoginController(LoginService loginService) {
this.loginService = loginService;
}
#PostMapping
public ResponseEntity<UserDTO> getUserDTOFrom(#Valid #RequestBody Credentials credentials) {
UserDTO userDTO = loginService.getUserDTOFrom(userForm);
// return response that depends on outcome in the login service
}
}
The issue arises when I want to logout previously logged in user. LoginContext is responsible for calling the logout method in the JAAS Login Module. For instance:
loginContext.logout();
The method in the JAAS Login Module:
public class JAASLoginModule implements LoginModule {
#Override
public boolean logout() {
subject.getPrincipals().remove(usernamePrincipal);
subject.getPrincipals().remove(passwordPrincipal);
return true;
}
}
I don't have the LoginContext in LogoutService and unable to completely clear the previously authenticated subject.
I tried to create a singleton bean to get the same instance of the LoginContext:
#Configuration
public class LoginContextBean {
#Lazy
#Bean
public LoginContext getLoginContext(Credentials credentials) throws LoginException {
System.setProperty("java.security.auth.login.config", "resources/configuration/jaas.config");
return new LoginContext("Login", new JAASCallbackHandler(credentials));
}
}
#Service
public class LoginService {
private final ObjectProvider<LoginContext> loginContextProvider;
#Autowired
public LoginService(ObjectProvider<LoginContext> loginContextProvider) {
this.loginContextProvider = loginContextProvider;
}
public UserDTO getUserDTOFrom(Credentials credentials) {
try {
LoginContext loginContext = loginContextProvider.getObject(credentials);
loginContext.login();
// construct UserDTO object.
} catch (LoginException e) {
LOGGER.error("Login Exception: {}", e.getMessage());
// construct UserDTO object.
}
// return UserDTO object.
}
}
#Service
public class LogoutService {
private final ObjectProvider<LoginContext> loginContextProvider;
#Autowired
public LogoutService(ObjectProvider<LoginContext> loginContextProvider) {
this.loginContextProvider = loginContextProvider;
}
public void performLogout() {
LoginContext loginContext = loginContextProvider.getObject();
try {
loginContext.logout();
} catch (LoginException e) {
LOGGER.error("Failed to logout: {}.", e.getMessage());
}
}
}
The solution is not particularly useful, since next / the same user to log in will get NPE on the LoginContext.
I read that HttpServletRequest's getSession().invalidate(); suppose to call the logout() of JAAS or that HttpServletRequest's logout() would do the job. But both methods have no effect. For instance:
#RestController
#RequestMapping("/logout")
public class LogoutController {
private final LogoutService logoutService;
#Autowired
public LogoutController(LogoutService logoutService) {
this.logoutService = logoutService;
}
#DeleteMapping
public ResponseEntity<Void> deleteJwt(#CookieValue("jwt_cookie") String jwtToken, HttpServletRequest request) throws ServletException {
request.getSession().invalidate(); // logout() is not called.
request.logout(); // logout() is not called.
return getResponse();
}
}
I want to get the hand on the previously created LoginContext when a user wants to log out but create a new one when another user tries to log in.
Please note that I am not using Spring Security.
EDIT:
One of the ideas was to use a singleton that will hold a Set of login contexts associated with the particular user. And then call and destroy them when the user logs out. A key for such a Set could be a JWT token or user id. After further thinking, it appeared to me that a user might have multiple sessions, and in this case, user id as a key will fail to serve its purpose. The second option is a JWT token, but there is a scenario when the future middleware will issue a new JWT token upon expiration, then my Set will have no way to return a valid login context.
After some research, my team decided that JAAS doesn't suit our needs. We are not using the complete functionality it has to offer, and it ties our hands rather than smoothing the developing process.
If you will encounter a similar issue, here is an explanation:
we are using WebSphere 8.5.5 that has the support of JAAS. It is possible to logout, but the price will be tying it to the application server. Considering that in our plans is to move from WebSphere, this implementation is not an option.
The link to one of such guides lies here.
There are two alternatives for the future:
Wrap it in Spring Security since it offers support for JAAS;
Replace the custom module entirely relying on Spring Security's
functionality.
After struggling with this for a few days (searching SO for similar questions, doing trial & error), I am tempted to give up...
So the problem is I have a REST service based on Spring Boot using Spring Security and JWT for authentication. Now I want to secure some of the methods to be only called by authorized people using the #PreAuthorize-annotation.
This seems to work partly because instead of calling the method Spring returns 404. I would have expected 403.
I have read this SO-question and tried the answers given there, but it did not help. I have moved the #EnableGlobalMethodSecurity(prePostEnabled = true)-Annotation from my SecurityConfiguration to the Application class as suggested elsewhere, still it does not work.
My security configuration looks like this:
#Configuration
#Profile("production")
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Value("${adDomain}")
private String adDomain;
#Value("${adUrl}")
private String adUrl;
#Value("${rootDn}")
private String rootDn;
#Value("${searchFilter}")
private String searchFilter;
private final AuthenticationManagerBuilder auth;
private final SessionRepository sessionRepository;
#Autowired
public SecurityConfiguration(AuthenticationManagerBuilder auth, SessionRepository sessionRepository) {
this.auth = auth;
this.sessionRepository = sessionRepository;
}
#Override
public void configure(WebSecurity webSecurity) throws Exception
{
webSecurity
.ignoring()
// All of Spring Security will ignore the requests
.antMatchers("/static/**", "/api/web/logout")
.antMatchers(HttpMethod.POST, "/api/web/login");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() // Using JWT there is no need for CSRF-protection!
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthorizationFilter(authenticationManagerBean(), sessionRepository));
}
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
ActiveDirectoryLdapAuthenticationProvider adProvider =
new ActiveDirectoryLdapAuthenticationProvider(adDomain, adUrl, rootDn);
adProvider.setConvertSubErrorCodesToExceptions(true);
adProvider.setUseAuthenticationRequestCredentials(true);
adProvider.setSearchFilter(searchFilter);
adProvider.setUserDetailsContextMapper(new InetOrgPersonContextMapper());
auth.authenticationProvider(adProvider);
return super.authenticationManagerBean();
}
}
The controller method looks like this
#RequestMapping(path = "/licenses", method = RequestMethod.GET)
#PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<?> getAllLicenses(#RequestParam("after") int pagenumber, #RequestParam("size") int pagesize
, #RequestParam("searchText") String searchText) {
List<LicenseDTO> result = ...
return new ResponseEntity<Object>(result, HttpStatus.OK);
}
I am quite sure I am missing something very simple, but I just cannot figure out what.
By the way: if the user requesting the licenses has the ADMIN role everything works as expected, so the problem is not a real 404.
You need to define the exceptionHandling at security configuration as follows,
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() // Using JWT there is no need for CSRF-protection!
.authorizeRequests()
.anyRequest().authenticated()
.and()
.exceptionHandling().accessDeniedHandler(new AccessDeniedExceptionHandler())
.and()
.addFilter(new JwtAuthorizationFilter(authenticationManagerBean(), sessionRepository));
}
You can define AccessDeniedExceptionHandler class as follows,
public class AccessDeniedExceptionHandler implements AccessDeniedHandler
{
#Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException ex) throws IOException, ServletException {
response.setStatus(HttpStatus.FORBIDDEN);
}
}
I finally found a solution fitting my purposes. I do not know if this is the best way to deal with this, but just adding an ExceptionHandler did the trick. Somewhere deep inside the filterchain the 403 mutates to 404 when there is no such handler in place.
Perhaps I am to dump to read and understand the documentation, but I did not find anything that suggest you have to do this. So maybe I am wrong solving the problem like this, but here is the code that did the trick (it is a really basic implementation that should be improved over time):
#ControllerAdvice
public class MyExceptionHandler {
#ExceptionHandler(Throwable.class)
#ResponseBody
public ResponseEntity<String> handleControllerException(Throwable ex) {
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
if(ex instanceof AccessDeniedException) {
status = HttpStatus.FORBIDDEN;
}
return new ResponseEntity<>(ex.getMessage(), status);
}
}
Global method security can be enabled with the help of annotation #EnableGlobalMethodSecurity(prePostEnabled=true) . The combination of this and #Preauthorize will create a new proxy for your controller and it will loose the Request mapping which will result in 404 Exception.
To handle this you can use the annotation #EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true) which is there in your SecurityConfiguration class.
Provided the details in another answer as well.
We are developing an application that uses OAuth 2 for two use cases:
Access to backend microservies (using client_credentials)
Authenticating the application's users (using authorization_code, so redirecting the users to Keycloak for login, roughly configured like shown in the tutorial).
While authenticating our users, we receive part of the information from the auth server (such as login) and the other part can be found in a local user table. What we like to do is to create a Principal object containing also the data from the local database.
PrincipalExtractor seems to be the way to go. Since we have to use manual OAuth configuration to not interfere with OAuth use case 1, we create it and set it:
tokenServices.setPrincipalExtractor(ourPrincipalExtractor);
The implementation basically does a DB lookup and returns a CustomUser object in the mapping function. Now although this seems to work (extractor is called), it is not persisted in the session correctly. So in many of our REST resource we are injecting the current user:
someRequestHandler(#AuthenticationPrincipal CustomUser activeUser) {
and receive null there. Looking into an injected Authentication it shows that it is an OAuth2Authentication object with the default Principal object (I think it is a Spring User / UserDetails). So null because it is not our CustomUser returned before.
Have we misunderstood the way PrincipalExtractor works? Can it be a misconfiguration of our filter chain because we have two different OAuth mechanisms in the same application as mentioned before? A breakpoint in Spring's Principal repository showed us that CustomUser is saved there, followed by a save with the original type which seems to overwrite it.
Ok, to answer my own question:
PrincipalExtractor seems to be the usual and standard way to customize the principal
It doesn't work in our case because we are using a JHipster application that simply overwrites the principal right after the login with it's own User. So all mapping in PrincipalExtractor is reset. If anyone has the same question: Look into UserService.
That's the downside of using generated code you don't know in detail I guess.
I can tell you how I managed to do something similar using JWT. If you aren't using JWT then I'm not sure if this will help.
I had a very similar issue in that my injected principal was only containing the username. Not null like yours, but obviously not what I wanted. What I ended up doing was extending both the TokenEnhancer and JwtAccessTokenConverter.
I use the TokenEnhancer to embed my extended principal of type CustomUserDetailsinside the JWT additional information.
public class CustomAccessTokenEnhancer implements TokenEnhancer {
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
Authentication userAuthentication = authentication.getUserAuthentication();
if (userAuthentication != null) {
Object principal = authentication.getUserAuthentication().getPrincipal();
if (principal instanceof CustomUserDetails) {
Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("userDetails", principal);
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
}
}
return accessToken;
}
}
And then manually extract the extended principal when building the Authentication object when processing an authenticated request.
public class CustomJwtAccessTokenConverter extends JwtAccessTokenConverter {
#Override
public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
OAuth2Authentication authentication = super.extractAuthentication(map);
Authentication userAuthentication = authentication.getUserAuthentication();
if (userAuthentication != null) {
LinkedHashMap userDetails = (LinkedHashMap) map.get("userDetails");
if (userDetails != null) {
// build your extended principal here
String localUserTableField = (String) userDetails.get("localUserTableField");
CustomUserDetails extendedPrincipal = new CustomUserDetails(localUserTableField);
Collection<? extends GrantedAuthority> authorities = userAuthentication.getAuthorities();
userAuthentication = new UsernamePasswordAuthenticationToken(extendedPrincipal,
userAuthentication.getCredentials(), authorities);
}
}
return new OAuth2Authentication(authentication.getOAuth2Request(), userAuthentication);
}
}
and the AuthorizationServer configuration to tie it all together.
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private DataSource dataSource;
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
CustomJwtAccessTokenConverter accessTokenConverter = new CustomJwtAccessTokenConverter();
accessTokenConverter.setSigningKey("a1b2c3d4e5f6g");
return accessTokenConverter;
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
#Bean
public TokenEnhancer tokenEnhancer() {
return new CustomAccessTokenEnhancer();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource).passwordEncoder(passwordEncoder());
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter()));
endpoints
.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancerChain)
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.passwordEncoder(passwordEncoder());
security.checkTokenAccess("isAuthenticated()");
}
}
I am then able to access my extended principal in my resource controller like this
#RestController
public class SomeResourceController {
#RequestMapping("/some-resource")
public ResponseEntity<?> someResource(Authentication authentication) {
CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
return ResponseEntity.ok("woo hoo!");
}
}
I am pretty new in Spring Security and I have the following problem.
I am working on a Spring Boot prject using Spring Security to protect all the resources into /Extranet/".
So basically my WebSecurityConfig configuration class contains this code:
#Configuration
#EnableWebSecurity
#ComponentScan(basePackageClasses = CustomUserDetailsService.class)
#EnableAutoConfiguration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/Extranet/**").access("hasRole('ROLE_USER')")
.anyRequest().permitAll()
.and()
.httpBasic()
.and()
.csrf().disable();
}
}
It works fine, infact to access to a resource as /Extranet/login I have to set the Basic Authentication and specify the correct username and password (performing the request using Postman tool to test it).
Ok, this works fine.
In my Spring Security configuration is involed this CustomUserDetails class that implements the Spring Security interface UserDetails.
public class CustomUserDetails extends User implements UserDetails {
private static final long serialVersionUID = 1L;
public CustomUserDetails(User user){
super(user);
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
for(UserRole role : this.getUserRoles() ){
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role.getName());
authorities.add(grantedAuthority);
}
return authorities;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
#Override
public String getUsername() {
return super.getUsername();
}
}
An instance of this object contains the user details of the user currently logged.
Ok my doubt is: how can I retrieve this object from a controller method? (I think that is should be into the context and that I can retrieve it in some way).
I have tryied to do in this way:
#RestController
#RequestMapping("/Extranet")
public class AccessController {
#RequestMapping(value = "/login", method = RequestMethod.POST)
public ResponseEntity<String> login(CustomUserDetails userInfo) {
System.out.println("login() START");
return ResponseEntity.ok("LOGGED IN");
}
}
but in this way I obtain an exception like this:
[ERROR] 2017-01-23 14:18:04 [com.betrivius.controller.exceptionHandler.ControllerExceptionHandler.handleException(ControllerExceptionHandler.java:106)] [http-nio-8080-exec-1] ControllerExceptionHandler - Request: http://localhost:8080/Extranet/login throws:
org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.betrivius.security.bean.CustomUserDetails]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.betrivius.security.bean.CustomUserDetails.<init>()
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:105) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
It seems that it can't instantialete the CustomUserDetails object. Why?
From what I know I can retrieve the CustomUserDetails object related to the logged user. How can I do it?
I also try to do in this way:
#RequestMapping(value = "/login", method = RequestMethod.POST)
public ResponseEntity<String> login(Principal principal) {
System.out.println("login() START");
return ResponseEntity.ok("LOGGED IN");
}
In this way the principal parameter is correctly instantiated and contains an instance of the previous CustomUserDetails object containing the information of the logged user.
So is it the correct way to access to the current logged user information?
Another doubt is: Why I can pass this Principal principal parameter to my login() method? What exactly happen under the hood? Who is that effectively pass it to the login() method?
I think that should happen something like this:
1) There is a POST HttpRequest toward the"/Extranet/login" resource. The dispatcher servlet send it to the login() method.
2) The Principal principal was put into the Spring Context after that the user was enabled for this resource (before that the controller method was called) so the Spring factory can retrieve it from the context and pass it to the login() method.
But I am absolutly not sure about it. How exactly works? What am I missing?
You probably need then #AuthenticationPrincipal annotation:
#RequestMapping(value = "/login", method = RequestMethod.POST)
public ResponseEntity<String> login(#AuthenticationPrincipal CustomUserDetails userInfo) {
System.out.println("login() START");
return ResponseEntity.ok("LOGGED IN");
}
If that still doesn't solve the problem, try debugging using the old method:
#RequestMapping(value = "/login", method = RequestMethod.POST)
public ResponseEntity<String> login() {
CustomUserDetails userInfo = (CustomerUserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
...
}