Spring Security multiple logged users fail - java

I am making a basic Tic tac toe Project with Spring , Spring Security and Hibernate. Application can save game for each logged user in database and allow to load it whenever we want. That is not a problem , but it appears when it comes to multi-threading. When i run single application in single browser window everything is working good. But when i open another window , two players are playing the same game.
I know it may be caused by bean singletons created by Spring but I am sure that it is not. To check currently logged user, i made an method to get him from SecurityContextHolder
private User getUserFromSpringContext() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String name = authentication.getName();
System.out.println("Currently logged users = " + name);
return userService.findUserByUsername(name);
}
When multiple users are logged in , that metod prints name of only one of them. I have no idea why. Here are some important code lines my Security configuration and userDetails classes:
Security Configuration:
#Autowired
UserDetailsService userDetailsService;
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userDetailsService);
builder.authenticationProvider(authenticationProvider());
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
auth.setUserDetailsService(userDetailsService);
return auth;
}
Custom Users Details Service
#Autowired
private UserService userService;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.findUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("Username not found");
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), true, true, true, true, getAuthoriries(user));
}
public void setUserService(UserService userService) {
this.userService = userService;
}
private List<GrantedAuthority> getAuthoriries(User user) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority(user.getRole().getRole()));
return authorities;
}
Does anybody knows cause of this problem?
During testing that I came up with another problem. When i click Logout , all users are logged out. I am posting here rest of my Spring Security Configuration.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/start", "/", "/login", "/registry","/success","/new").permitAll()
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.antMatchers("/**").access("hasAnyRole('ROLE_USER','ROLE_ADMIN')")
.and().formLogin().loginPage("/login").defaultSuccessUrl("/user")
.and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.and().csrf()
.and().exceptionHandling().accessDeniedPage("/access_denied");
}
What can be a problem?

SecurityContextHolder gives you access to the security context associated with the current thread, thus only the current user - the one whose request triggered the call to getAuthentication(), so it's behaving exactly the way it should.
If you, on the other hand, want all active sessions (i.e. all logged in users) you should inject the SessionRegistry and call sessionRegistry.getAllPrincipals() on it.
The details are already given here.

Related

Updated password not picked up after InMemoryUserDetailsManager updateUser call

Hi I have a Rest WS using WebSecurityConfigurerAdapter to implement HTTP Basic auth.
The password is allowed to be updated and I need to let the WS to pick up updated password without restarting server
Following are the codes:
SecurityConfig
// init a user with credentials admin/password
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
//disable csrf
.csrf().disable()
//authentic all requests
.authorizeRequests().anyRequest().authenticated().and().httpBasic()
//disable session
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(inMemoryUserDetailsManager());
}
#Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
Properties users = new Properties();
users.put("admin", "password,USER,enabled");
return new InMemoryUserDetailsManager(users);
}
}
The controller that will update password
#RestController
public class someController{
#Autowired
public InMemoryUserDetailsManager inMemoryUserDetailsManager;
// update password from password -> pass
#RequestMapping(...)
public updatePass(){
ArrayList<GrantedAuthority> grantedAuthoritiesList = new ArrayList<>();
grantedAuthoritiesList.add(new SimpleGrantedAuthority("USER"));
this.inMemoryUserDetailsManager.updateUser(new User("admin", "pass", grantedAuthoritiesList));
}
// another way that also doesn’t work
#RequestMapping(...)
public newUpdate(){
ArrayList<GrantedAuthority> grantedAuthoritiesList = new ArrayList<>();
grantedAuthoritiesList.add(new SimpleGrantedAuthority("USER"));
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("admin", "pass",
grantedAuthoritiesList);
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(auth);
SecurityContextHolder.setContext(context);
}
}
After calling updatePass() with credential admin/password for the first time, I can see that the password has been updated to "pass" in debugger
I assume that if I'm to call updatePass() again, I should use admin/pass. However it turned out to be still using the old admin/password.
Sources I referred to when writing this code source1 source2
*I'm using Advance Rest Client to make the calls
When you update the password, you have to set the UserDetails in springSecurityContext object if the user is authenticated.
instead of using SecurityContext, I overwrote function loadUserByUsername of interface UserDetailsService to let spring security always pick up the latest pwd from DB.

Spring Security with JWT token is loading spring UserDetails object on every request

I am learning spring security with JWT token and spring boot. I have implemented it properly and it is working fine. But I have one doubt in how JwtRequestFilter works. I have gone through couple of websites to understand spring security with spring boot and found same thing. So let me go to main doubt.
I am adding JwtRequestFilter file below.
JwtRequestFilter.java
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private JwtUserDetailsService jwtUserDetailsService;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
// JWT Token is in the form "Bearer token". Remove Bearer word and get
// only the Token
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
} else {
logger.warn("JWT Token does not begin with Bearer String");
}
// Once we get the token validate it.
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
// This below line is calling on every request
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
// if token is valid configure Spring Security to manually set
// authentication
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// After setting the Authentication in the context, we specify
// that the current user is authenticated. So it passes the
// Spring Security Configurations successfully.
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
filterChain.doFilter(request, response);
}
}
As highlighed to validate token we have to provide spring UserDetails object and we are getting spring UserDetails object from jwtUserDetailsService. So every request this filter will call then token verification will perform and we have to call jwtUserDetailsService on every request.
My doubt is inside my jwtUserDetailsService I am adding couple of validation and adding user privileges. So on every request below steps are repeated in jwtUserDetailsService.
Get user using username from DB.
Get user role
Get user privileges from DB.
Assign privileges to userDetails.
JwtUserDetailsService.java
#Service("jwtUserDetailsService")
#Transactional
public class JwtUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Autowired
private IUserService service;
#Autowired
private MessageSource messages;
#Autowired
private RoleRepository roleRepository;
#Override
public UserDetails loadUserByUsername(String email)
throws UsernameNotFoundException {
User user = userRepository.findByEmail(email);
if (user == null) {
return new org.springframework.security.core.userdetails.User(
" ", " ", true, true, true, true,
getAuthorities(Arrays.asList(
roleRepository.findByName("ROLE_USER"))));
}
return new org.springframework.security.core.userdetails.User(
user.getEmail(), user.getPassword(), user.isEnabled(), true, true,
true, getAuthorities(user.getRoles()));
}
private Collection<? extends GrantedAuthority> getAuthorities(
Collection<Role> roles) {
return getGrantedAuthorities(getPrivileges(roles));
}
private List<String> getPrivileges(Collection<Role> roles) {
List<String> privileges = new ArrayList<>();
List<Privilege> collection = new ArrayList<>();
for (Role role : roles) {
collection.addAll(role.getPrivileges());
}
for (Privilege item : collection) {
privileges.add(item.getName());
}
return privileges;
}
private List<GrantedAuthority> getGrantedAuthorities(List<String> privileges) {
List<GrantedAuthority> authorities = new ArrayList<>();
for (String privilege : privileges) {
authorities.add(new SimpleGrantedAuthority(privilege));
}
return authorities;
}
}
So on every request these queries are executing. Is there any better way of doing this? Because once I am adding user privileges in spring UserDetails object why we need to do that again on every request. Or those have scope of request only. I have worked on spring mvc and once we add privileges in spring UserDetails object it will be there until I am hitting logout means It will be there in spring security context until we remove it. Will it be same in spring boot? If I am adding role and privileges details once in spring UserDetails object why we need to add it again?
So every request this filter will call then token verification will
perform and we have to call jwtUserDetailsService on every request.
This can't be correct since you have a condition if (SecurityContextHolder.getContext().getAuthentication() == null).
So the first time the token was validated, you query your user details service, fetch all grants and set them to the Security context (you are already doing it: SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);).
Furthermore, with the JWT auth you usually don't even need to access any user details service since all grants ideally should be contained in the token itself. So the only thing you need to do is validating token's signature.
Once an user logs in his authentication is established, so you don't need to do db call again, after login in every request user should be checked for authorization only with the roles being set in the token during authentication, you need to validate the token is not tampered in every request
instead of creating userdetails by loading user detail from db call
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
you could also encode the user's username and roles inside JWT claims
and create the UserDetails object by parsing those claims from the JWT.

spring security user doesn't load on authenticationmanagerbuilder

Hello fellow junior developers, in a project with spring framework + hibernate + spring security + jsf I have quite a problem, when checking a logged user into the builder of spring security, I can easily retrieve the user from the .xhtml and check the database but when I am passing the user to spring security it doesn't activate.
The part on my customUserDetail where I transfer the user to the SecurityConfig class is the following
public org.springframework.security.core.userdetails.UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
// TODO Auto-generated method stub
System.out.println("das"+ usuario_login.getDas());
System.out.println("pass"+ usuario_login.getPassword());
List<GrantedAuthority> authorities = buildUserAuthority(usuario_login.getRoles());
return buildUserForAuthentication(usuario_login, authorities);
//SecurityConfig sec = new SecurityConfig().configureGlobal(auth);
}
private User buildUserForAuthentication ( Usuarios user, List<GrantedAuthority> authorities) {
return new User(user.getDas(),user.getPassword(),true,true,true,true,authorities);
}
private List<GrantedAuthority> buildUserAuthority(Roles roles) {
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
setAuths.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths);
return Result;
}
the Rol of the user is hardcoded as Role_admin for now. The thing is that even when debugging, everything works fine while doing these 3 previous methods, but then instead loading the configure (AuthenticationManagerBuilder auth) method of SecurityConfig.class , it just does nothing.
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// TODO Auto-generated method stub
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
When I run the application the first thing it does is load the SecurityConfig.class and its method, but then when the view of login appear and all the verification with the database is done, that builder in SecurityConfig is never used again.

Spring security custom UserDetails not authenticating

while experimenting around with spring boot, security, and data.
i just came across this scenario:
i use H2 in memory DB and poblate it with one user with liquibase on startup
with username and password.
now i want spring security to authenticate against H2. for that purpose i have this code:
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsServiceImp);
}
and im implementing the userDetails as follows:
#Override
public UserDetails loadUserByUsername(String username) {
//this works, the user with pass is pulled
com.fix.demo.logic.user.User byUsername =
userRepository.findByUsername(username);
if (byUsername == null) {
System.out.println("No user found with username: ");
return null; //trow ex here
}
User user = new User(byUsername.getUsername(),
byUsername.getPassword(), true, true,
true, true, getAuthorities(Collections.singletonList("user")));
//System.out.println(user.toString());
//System.out.println(byUsername.toString()+ " "+byUsername.getPassword());
return user;
}
but my tests keep failing with
Authentication should not be null
and trying to log in will give me
bad credentials
what is necessary for my custom implementation of UserDetailsService to work?
this is the failing test:
#Test
public void loginWithValidUserThenAuthenticated() throws Exception {
FormLoginRequestBuilder login = formLogin()
.user("admin")
.password("root");
mockMvc.perform(login)
.andExpect(authenticated().withUsername("admin"));
}
One of the reasons is, the password might my encoded and you need to tell spring security to use an encoder. Add the following line to the configure override.
auth.setPasswordEncoder(passwordEncoder());
define the passwordEncoder bean.
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

How to authenticate user manually in spring boot?

I have two tables 'user' and 'role'.I want to create a login api (e.g '/login') which will take username and password as a json data. I want to check if given credential is a valid credential and if it is,then I want to set the user as authenticated user so that he/she may have the protected resources. I am new to spring boot framework and I don't know how to do so.I have read the offical documentation but cannot find any resources.Could someone help me on this?
You have number of choices to implement such authentication in Spring.
Case 1:- If you are building REST services then you can implement security in following ways:
i) - you can use Basic-Authentication to authenticate your user.
ii) - you can use OAuth2 to authenticate and authorize your user.
Case 2: If you are building web application
i) - you can use auth token (in case of Single page application SPA)
ii) - you can use session based authentication (traditional login form and all)
I Guess you are in beginner mode so i will recommend you to firstly understand the control flow user authentication in web app via login form. So Let's go through some code.
I'm assuming that you have set a basic spring project and now you are implementing security.
USER - Hibernate entity for your user table;
ROLE - Hibernate entity for your role table
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthProvider customAuthProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
// everyone is allowed tp view login page
http.authorizeRequests().antMatchers("/login").permitAll().and();
http.authorizeRequests().antMatchers("custom_base_path" + "**").authenticated().and().
formLogin().loginPage("/loginForm).loginProcessingUrl("/loginUser")
.usernameParameter("username").passwordParameter("password")
.defaultSuccessUrl("custom_base_path+ "home", true);
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthProvider);
}
//CustomAuthProvider
#Component
public class CustomAuthentiationProvider implements AuthenticationProvider{
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String userid = authentication.getName();
String password = authentication.getCredentials().toString();
Authentication auth = null;
try {
//write your custom logic to match username, password
boolean userExists = your_method_that_checks_username_and_password
if(userExists ){
List<Role> roleList= roleDao.getRoleList(userid);
if (roleList == null || roleList.isEmpty()) {
throw new NoRoleAssignedException("No roles is assigned to "+userid);
}
auth = new UsernamePasswordAuthenticationToken(userid, password,getGrantedAuthorities(roleList));
}
} catch (Exception e) {
log.error("error", e);
}
return auth;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
public List<GrantedAuthority> getGrantedAuthorities(List<Role> roleList) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (Role role : roleList) {
authorities.add(new SimpleGrantedAuthority(role.getRoleName());
}
return authorities;
}
}
NOTE: Please consider these codes to understand the logic of authentication. don't consider as perfect code(Not for production env.). You can ping me anytime i'll suggest you more about that.

Categories