In short:
I want to know if a user - given by the username - is authenticated or not.
I want to know this for Username / password auths given by org.springframework.security.authentication.UsernamePasswordAuthenticationToken
but also for remember me users given by org.springframework.security.authentication.RememberMeAuthenticationToken
The username / password part is easy:
class someService {
def userDetailsService
def sessionRegistry
boolean hasUserUserNameAndPasswordAuth(String username){
def principal = userDetailsService.loadUserByUsername(theUser.email, false)
userSession = sessionRegistry.getAllSessions(principal, false).find { !it.isExpired() }
if (userSession)
return true
else
return false
}
}
All users which are authenticated by username and password will return true on the function above. But all remembermy users will return false.
Even this method call sessionRegistry.getAllPrincipals() will return a list which does not contains principals that are authenticated by remember me token.
Why does the sessionRegistry not return users with a remember me authentication? Don't they also have a session?!
To get remember me authentifications, I played around with the rememberMeServices class. This class has a method called autoLogin(request, response) that returns the authentication of the current request. But this doesn't help me because I want to know if a user given by a username has a valid authentification, not the current one)
I think I have 3 options:
a) Find you why sessionRegistry does not return sessions for remember me authentifications
b) find a service that returns all principals that have a valid (from the expire date point of view) token
c) write a filter that intercepts every request and creates a new session - that can be found by the sessionRegistry - when a user has a remember me auth based on calling rememberMeServices.autoLogin(request, response).
What do you think?
Related
This is my first Question ever here on SO, it was helpfull and saved me lots of time, but now I can't find any solution to my problem.
As I'm rather new to spring and espacially to spring-security, I'm stuck with something that might be easy if i had more knowledge.
I have an existing Application that uses a local user database. It uses a custom UserDetails implementation that works if used with user:password authentification through a login form.
Here is the current setup:
public class SecurityContext extends WebSecurityConfigurerAdapter {
....
#Autowired
public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider()).userDetailsService(userDetailsService());
}
#Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider result = new DaoAuthenticationProvider();
result.setUserDetailsService(userDetailsService());
result.setPasswordEncoder(passwordEncoder());
return result;
}
#Override
#Bean
public GatesUserDetailsService userDetailsService() {
GatesUserDetailsService result = new GatesUserDetailsService();
result.setClientService(clientService);
result.setAccountService(accountService);
result.setCardService(cardService);
result.setPersonService(personService);
result.setAccountPropertyService(accountPropertyService);
result.setLoginAttemptService(loginAttemptService);
return result;
}
Now I want to use SSO from an external IDP that speaks OpenIdConnect.
Going through the documentation I was able to get this up and running in a "default" manner. That is, at the and of my process a get a user that is an Instance of OidcUser. I need that user to be either extended or incorporate the existing userDetails.
The documentation (Spring Boot and OAuth2) recommends to
Implement and expose OAuth2UserService to call the Authorization
Server as well as your database. Your implementation can delegate to
the default implementation, which will do the heavy lifting of calling
the Authorization Server. Your implementation should return something
that extends your custom User object and implements OAuth2User.
I was able to introduce my own Oauth2UserService that gets called right at the and of the authentification by setting:
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
.and()
.oauth2Login()
.failureHandler(authenticationFailureHandler())
.successHandler(authenticationSuccessHandler())
.userInfoEndpoint()
.userService(this.oauth2UserService())
.oidcUserService(this.oidcUserService());}
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
final OidcUserService delegate = new OidcUserService();
return (userRequest) -> {
OidcUser oidcUser = delegate.loadUser(userRequest);
//..DO some additional Stuff check against external Server
//Here I could load my custom userDetails
GatesUserDetails userDetails = (GatesUserDetails) userDetailsService.loadUserByUsername("131:" + username);
....
But I have now Idea how to make my customUser a vaild return to my function.
I tried to implement the OidcUser Interface in my userDetails, but still it does not work.
Any hint (even to a more understandable doc) would be highly appreciated.
EDIT
To clarify things, I implemented the oidcUser Interface as stated in the docs along with the necessary implementations (getAttribute, getAttributes, getAuthorities) but still I could not use this as the return type would still be our GatesUserDetails, no way (for me) to cast it to oidcUser
Have the same problem with spring-security-oauth2-client-5.6.2, after hours google and debugger it solved.
First, make sure your UserInfo entrypoint is correct in case you own the
Auth server.
Plus requested scopes contains any of profiles not
only openid.
Logic found here: OidcUserService::shouldRetrieveUserInfo
private boolean shouldRetrieveUserInfo(OidcUserRequest userRequest) {
// Auto-disabled if UserInfo Endpoint URI is not provided
ProviderDetails providerDetails = userRequest.getClientRegistration().getProviderDetails();
if (StringUtils.isEmpty(providerDetails.getUserInfoEndpoint().getUri())) {
return false;
}
// The Claims requested by the profile, email, address, and phone scope values
// are returned from the UserInfo Endpoint (as described in Section 5.3.2),
// when a response_type value is used that results in an Access Token being
// issued.
// However, when no Access Token is issued, which is the case for the
// response_type=id_token,
// the resulting Claims are returned in the ID Token.
// The Authorization Code Grant Flow, which is response_type=code, results in an
// Access Token being issued.
if (AuthorizationGrantType.AUTHORIZATION_CODE
.equals(userRequest.getClientRegistration().getAuthorizationGrantType())) {
// Return true if there is at least one match between the authorized scope(s)
// and accessible scope(s)
return this.accessibleScopes.isEmpty()
|| CollectionUtils.containsAny(userRequest.getAccessToken().getScopes(), this.accessibleScopes);
}
return false;
}
Hope this could help someone.
Based on my understanding, there are a number of different ways to retrieve the authenticated username in Spring Security.
I'm currently grabbing the username by included the Principal as a controller method argument:
#RequestMapping(value = "/dashboard", method = RequestMethod.GET)
public ModelAndView displayHomePage(ModelAndView modelAndView, Principal principal) {
modelAndView.addObject("email", principal.getName());
// Render template located at src/main/resources/templates/dashboard.html
modelAndView.setViewName("dashboard");
return modelAndView;
}
Does Spring Security offer an easy way for me to store the User object into the session so it can be easily retrieved by any controller method?
I want to avoid performing a DB lookup each time:
// Lookup user in database by e-mail
User user = userService.findUserByEmail(principal.getName());
I'm using Spring Security 4.2.
Spring Security provides you with a static method for quickly and easy access:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String name = auth.getName();
Or
User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String name = user.getUsername();
Maybe you would like do this in a base abstract class
public abstract class BaseController {
protected User getCurrentUser() {
return (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
}
...
public YourController extends BaseController {
...
}
Update
If you want to store the current authenticated user in session, then you need store only first time in a object as suggested by #gkatzioura.
#Component
#Scope("session")
public class MySessionInfo {
private User user;
protected User getCurrentUser() {
if (user == null) {
user = userService.findUserByEmail(SecurityContextHolder.getContext().getAuthentication().getPrincipal().getName());
}
return user;
}
}
You can inject this bean in yours controllers like
#Autowired
private MySessionInfo mySessionInfo;
You must take care about cases when user is not logged, but this is another problem.
You can always use the methods that spring security provides to get basic information such as name, authorities and everything provided by the Authentication.class.
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
authentication.getAuthorities();
authentication.getName();
But if you want more information, using a session bean to store the information is also a good idea.
#Component
#Scope("session")
public class UserInfo { .. }
Creating a simple filter for trivial web application.
login
register
home
feature
if the user has not logged in using the login name "admin", or tries to access the /home resource or /feature resource, they should be routed back to the login page.
However, it appears I am running into a redirect loop problem. What is the problem with this approach. I feel it is incorrect solution approach for such a requirement.
public class LoginInterceptor extends HandlerInterceptorAdapter {
private static final Logger logger = Logger.getLogger("LoginInterceptor.class");
// to be used checking session management for user.
#Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse response,
Object handler) throws Exception {
logger.log(Level.INFO, "[][][][][] +++ prehandle");
if (request.getAttribute("username") != null) {
if (request.getAttribute("username").equals("admin")) {
return true;
} else
response.sendRedirect("/sessionmanagement/login");
return false;
} else if (request.getAttribute("username") == null & !request.getRequestURI().equals("/login")) {
// return false and redirect to login.
response.sendRedirect("/sessionmanagement/login");
return false;
}
// return false and redirect to login.
response.sendRedirect("/sessionmanagement/register");
return false;
}
}
if you are using spring you can use the spring security to do that, your user can have ROLES, so you won't be checking just against the username, https://spring.io/guides/gs/securing-web/ here is a guide that you can go over it, it is a pretty straight forward , so your methods you have a annotation to check what is the authority of the user.
here another thing that can help you : Spring Security with roles and permissions
http://www.journaldev.com/8748/spring-security-role-based-access-authorization-example
I have two web services based on Spring boot, REST and MVC. The first service(DesignService) project calls the second service(UserService) project passing a email as parameter and gets the user object(User.java) back. How can I handle if no user is found.
DesignService project
repository.java
#Autowired
RestTemplate restTemplate;
getUser(String email){
restTemplate.postForObject("url for User service",email,User.class); //calls User service for Object[1]
}
UserService
UserController.java
#RequestMapping()
public ResponseEntity<User> getUser(#RequestBody String email){
User user = repository.getUser(email); //return a user object from DB
return new ResponseEntity<User>(user);[2]
}
Repository.java
public User getUser(String email){
//query db for user
return user; //what to return if no user is found [3]
}
what to do at point [3] if user not found .throw exception or return empty user object?
what to do at [2] any excpetion handling / http status code / ... ??
how do I handle these exceptions at [1]. I dont think checking for user as null is a good idea.
* excuse me if am wrong in some syntax as I have typed it directly .
Return null, or if you're on Java 8, an empty Optional
If no user is found, return an empty Response with a 404 (NOT_FOUND) status, i.e.
new ResponseEntity(HttpStatus.NOT_FOUND);
Catch org.springframework.web.client.HttpClientErrorException
Finally, why are you POSTing to an endpoint that only returns a resource? You should be doing a GET, and from the looks of it, your request mapping should only support GET (method = RequestMethod.GET).
Looking to protect pages in a basic java spring application based on a token. After the token is consumed I would need the application to know the token was valid at some point and then put some time to live on that session. Below is the controller I have to consume the token.
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String login(ModelMap model, #RequestParam(value = "token", required = false) String token) {
if(token==null) {
return "redirect:403";
} else if(token.isEmpty()) {
return "redirect:403";
} else {
//perform token WS call to validate the token
return "redirect:home";
}
}
#RequestMapping(value = "/403", method = RequestMethod.GET)
public ModelAndView accesssDenied(Principal user) {
ModelAndView model = new ModelAndView();
model.addObject("msg",
"You do not have permission to access this page!");
model.setViewName("403");
return model;
}
After performing some check on the token how can I protect all of the subsequent pages? Id also like to be able to secure api calls as well. Can anyone point me in the direction of the spring component?
I think you should take a look at Spring Security instead of rolling your own solution - it is built for handling authentication.
What you especially should look at is session management which sounds like what you're trying to do here.
Depending on how your users get their token you might have to implement your own authentication manager and/or login flow, though the default ones cover a lot of common cases too.
Once you have Spring Security set up and your session management working you would protect URLs either by annotating the controller methods:
#RequestMapping("/api/protected")
#PreAuthorize("hasRole('ROLE_USER')")
public String myProtectedController(Authentication authentication, Model model) {
// User will be authenticated here
}
or by registering them into the HTTP security configuration:
#Configuration
public class SecurityConfig extends WebSecurityConfigurationAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
// Everyone can acess /login
.antMatchers("/login").permitAll()
// Only authorized users can access URLs under /api/
.antMatchers("/api/**").access("hasRole('ROLE_USER')")
}
}
Of course, in your case you might use something other than ROLE_USER since you may or may not have actual users but something else in your session that you can use.