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.
Related
I want to rewrite my Vaadin application to Vaadin 21.
With the Vaadin starter builder (https://vaadin.com/start) I created a simple app.
Currently my main struggle is to apply my simple CustomAuthenticationProvider to the Security manager to able to use the #RolesAllowed({ "user", "admin","USER"}) annotation.
Main problem that my AuthToken is generated somewhere else...
Its generate somewhere an empty Granted Authrities and ignore my custom AuthProvider code.
Question:
How to nicely handle role based access control?
Where I can use this annotation correctly:
#RolesAllowed({ "user", "admin","USER"})
public class ProfileView extends VerticalLayout {
Console after login:
UsernamePasswordAuthenticationToken [Principal=c.farkas, Credentials=[PROTECTED], Authenticated=false, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=DDE103F559B2F64B917753636B800564], Granted Authorities=[]]
xxx[USERcica, admin, USER]
??UsernamePasswordAuthenticationToken [Principal=c.farkas, Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[USERcica, admin, USER]]
SecurityConfiguration.java
#EnableWebSecurity
#Configuration
public class SecurityConfiguration extends VaadinWebSecurityConfigurerAdapter {
#Autowired
private RequestUtil requestUtil;
#Autowired
private VaadinDefaultRequestCache vaadinDefaultRequestCache;
#Autowired
private ViewAccessChecker viewAccessChecker;
#Autowired
CustomAuthenticationProvider customAuthenticationProvider;
public static final String LOGOUT_URL = "/";
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// super.configure(http);
http.csrf().ignoringRequestMatchers(requestUtil::isFrameworkInternalRequest);
// nor with endpoints
http.csrf().ignoringRequestMatchers(requestUtil::isEndpointRequest);
// Ensure automated requests to e.g. closing push channels, service
// workers,
// endpoints are not counted as valid targets to redirect user to on
// login
http.requestCache().requestCache(vaadinDefaultRequestCache);
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry urlRegistry = http
.authorizeRequests();
// Vaadin internal requests must always be allowed to allow public Flow
// pages
// and/or login page implemented using Flow.
urlRegistry.requestMatchers(requestUtil::isFrameworkInternalRequest).permitAll();
// Public endpoints are OK to access
urlRegistry.requestMatchers(requestUtil::isAnonymousEndpoint).permitAll();
// Public routes are OK to access
urlRegistry.requestMatchers(requestUtil::isAnonymousRoute).permitAll();
urlRegistry.requestMatchers(getDefaultHttpSecurityPermitMatcher()).permitAll();
// all other requests require authentication
urlRegistry.anyRequest().authenticated();
// Enable view access control
viewAccessChecker.enable();
setLoginView(http, LoginView.class, LOGOUT_URL);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// Custom authentication provider - Order 1
auth.authenticationProvider(customAuthenticationProvider);
// Built-in authentication provider - Order 2
/* auth.inMemoryAuthentication().withUser("admin").password("{noop}admin#password")
// {noop} makes sure that the password encoder doesn't do anything
.roles("ADMIN") // Role of the user
.and().withUser("user").password("{noop}user#password").credentialsExpired(true).accountExpired(true)
.accountLocked(true).roles("USER");*/
}
#Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
web.ignoring().antMatchers("/images/*.png");
}
}
CustomAuthenticationProvider.java
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
System.out.println(authentication);
try {
// LdapContext ldapContext =
ActiveDirectory.getConnection(username, password);
List<GrantedAuthority> authorityList = new ArrayList<GrantedAuthority>();
authorityList.add(new SimpleGrantedAuthority("USER" + "cica"));
authorityList.add(new SimpleGrantedAuthority("admin"));
authorityList.add(new SimpleGrantedAuthority("USER"));
System.out.println("xxx"+authorityList.toString());
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
username, password, authorityList);
System.out.println("??" + usernamePasswordAuthenticationToken);
String id = VaadinSession.getCurrent() != null ? VaadinSession.getCurrent().getSession().getId() : "";
return usernamePasswordAuthenticationToken;
} catch (NamingException e) {
// e.printStackTrace();
// throw new CortexException("Authentication failed");
throw new BadCredentialsException("Authentication failed");
}
}
#Override
public boolean supports(Class<?> aClass) {
return aClass.equals(UsernamePasswordAuthenticationToken.class);
}
}
You must add the ROLE_ prefix to tell Spring Security that the GrantedAuthority is of type role.
authorityList.add(new SimpleGrantedAuthority("ROLE_USER" + "cica"));
authorityList.add(new SimpleGrantedAuthority("ROLE_admin"));
authorityList.add(new SimpleGrantedAuthority("ROLE_USER"));
I have built a Java application with the REST API convention. I working on endpoint which returns objects only if object is connected with user by common id in database(ManyToOne annotation). In order to achieve that i need current logged user id for comapring it with object's user id. If Ids are the same, endpoint returns data. I know solutions as "Principal" or "Authentication" classes but they provide everything except of "id". I used spring security http basic for authentication.
My authentication classes:
#Component
public class CustomAuthenticator implements AuthenticationProvider {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
#Autowired
public CustomAuthenticator(UserRepository userRepository, #Lazy PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String login = authentication.getName();
String password = authentication.getCredentials().toString();
User user = userRepository.findByLogin(login).orElseThrow(() -> new EntityNotFoundException("User not found"));
if (!passwordEncoder.matches(password, user.getPassword())) {
throw new BadCredentialsException("Bad credentials");
}
return new UsernamePasswordAuthenticationToken(login, password, convertAuthorities(user.getRoles()));
}
private Set<GrantedAuthority> convertAuthorities(Set<UserRole> userRoles) {
Set<GrantedAuthority> authorities = new HashSet<>();
for (UserRole ur : userRoles) {
authorities.add(new SimpleGrantedAuthority(ur.getRole().toString()));
}
return authorities;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
SECURITY CONFIG CLASS:
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final CustomAuthenticator customAuthenticator;
public SecurityConfig(CustomAuthenticator customAuthenticator) {
this.customAuthenticator = customAuthenticator;
}
#Bean
public PasswordEncoder passwordEncoder() {
PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
return passwordEncoder;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/api").permitAll()
.antMatchers("/api/register").permitAll()
//TODO everybody now has access to database, change it later
.antMatchers("/h2-console/**").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic();
http
.csrf().disable()
.headers().frameOptions().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticator);
}
}
Does someone know how to resolve that problem ?
You can use UserDetails class and set id for the username field, this class provides by spring security.
If you don't want that solution, you can create a Subclass extend UserDetails class and decide an id field. When receiving the request, parse principal to UserDetails or subclass extends UserDetails to get the id
Ex:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
UserDetails userPrincipal = (UserDetails)authentication.getPrincipal();
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.
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.
I am using DaoAuthenticationProvider for authenciation but when I submit form loadUserByUsername is called twice by super.authenticate(authentication) intially it throws BadCredentialsException and then next time it login successfully
This process is working fine if I do not use passwordencoder but when I use it loadUserByUsername method is called twice.
Below is my code:
SecurityConfig
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("authenticationProvider")
AuthenticationProvider authenticationProvider;
#Autowired
#Qualifier("userDetailsService")
UserDetailsService userDetailsService;
#Bean
public PasswordEncoder passwordEncoder() {
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(authenticationProvider)
.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/admin/**")
.access("hasRole('ROLE_ADMIN')").and().formLogin()
.loginPage("/login").failureUrl("/login?error")
.usernameParameter("username").passwordParameter("password")
.and().logout().logoutSuccessUrl("/login?logout").and().csrf()
.and().exceptionHandling().accessDeniedPage("/403");
}
}
Authentication class
#Component("authenticationProvider")
public class LimitLoginAuthenticationProvider extends DaoAuthenticationProvider {
#Autowired
#Qualifier("userDetailsService")
#Override
public void setUserDetailsService(UserDetailsService userDetailsService) {
super.setUserDetailsService(userDetailsService);
}
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
try {
System.out.println("inside authenticate");
Authentication auth = super.authenticate(authentication);
return auth;
} catch (BadCredentialsException be) {
System.out.println("First call comes here ");
throw be;
} catch (LockedException e) {
throw e;
}
}
}
MyUserdetailsService class implments UserDetailsService
#Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
#Autowired
private UserDao userDao;
/* below method is called twice if I am using passwordencoder,
initially authentication fails and then again immediately
on second call authentication succeed */
#Transactional(readOnly=true)
#Override
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
com.mkyong.users.model.User user = userDao.findByUserName(username);
List<GrantedAuthority> authorities = buildUserAuthority(user.getUserRole());
return buildUserForAuthentication(user, authorities);
}
private User buildUserForAuthentication(com.mkyong.users.model.User user, List<GrantedAuthority> authorities) {
MyUserDetails myUserDetails = new MyUserDetails (user.getUsername(), user.getPassword(), user.isEnabled(), user.isAccountNonExpired(), user.isAccountNonLocked(), user.isCredentialsNonExpired(), user.getEmailId(),authorities);
return myUserDetails;
}
private List<GrantedAuthority> buildUserAuthority(Set<UserRole> userRoles) {
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
// Build user's authorities
for (UserRole userRole : userRoles) {
setAuths.add(new SimpleGrantedAuthority(userRole.getRole()));
}
List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths);
return Result;
}
}
Can some please help me. I believe there is some change needed in SecurityConfig class but exactly where I am not able to figure out.
Finally after help from java_dude and SergeBallesta I got the resolution for my query.
After a lot of debug I saw that when isPasswordValid method was getting called inside DaoAuthenticationProvider class instead of calling method 1 it was calling method 2 from org.springframework.security.authentication.encoding.PlaintextPasswordEncoder which one is depreciated and on second call it was calling proper isPasswordValid
method 1.
Method 1
public boolean isPasswordValid(String encPass, String rawPass, Object salt) {
checkSalt(salt);
return delegate.matches(rawPass, encPass);
}
Method 2
public boolean isPasswordValid(String encPass, String rawPass, Object salt) {
String pass1 = encPass + "";
// Strict delimiters is false because pass2 never persisted anywhere
// and we want to avoid unnecessary exceptions as a result (the
// authentication will fail as the encodePassword never allows them)
String pass2 = mergePasswordAndSalt(rawPass, salt, false);
if (ignorePasswordCase) {
// Note: per String javadoc to get correct results for Locale insensitive, use English
pass1 = pass1.toLowerCase(Locale.ENGLISH);
pass2 = pass2.toLowerCase(Locale.ENGLISH);
}
return PasswordEncoderUtils.equals(pass1,pass2);
}
To work authentication properly just add below code in your SecurityConfig class in addittion to my current code in question.
#Bean
public DaoAuthenticationProvider authProvider() {
// LimitLoginAuthenticationProvider is my own class which extends DaoAuthenticationProvider
final DaoAuthenticationProvider authProvider = new LimitLoginAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
** and change this method code**
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider())
.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}