Vaadin4Spring's ManagedSecurity: How to update user list? - java

I'm using Vaadin 7.5.6, Vaadins Spring 1.0.0, the Vaadin4Spring Managed Security Extension 0.0.7-SNAPSHOT and Tomcat8.
Currently, I got a configuration class which implements the AuthenticationManagerConfigurer interface:
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.vaadin.spring.security.annotation.EnableVaadinManagedSecurity;
import org.vaadin.spring.security.config.AuthenticationManagerConfigurer;
import com.vaadin.server.CustomizedSystemMessages;
import com.vaadin.server.SystemMessages;
import com.vaadin.server.SystemMessagesInfo;
import com.vaadin.server.SystemMessagesProvider;
import de.blume2000.kiss.hibernate.dto.User;
import de.blume2000.kiss.hibernate.services.UserService;
import de.blume2000.kiss.utils.EncryptionUtil;
#Configuration
#EnableVaadinManagedSecurity
public class SecurityConfiguration implements AuthenticationManagerConfigurer
{
#Autowired
UserService userService;
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception
{
List<User> users = userService.findAll();
if (users == null)
return;
for (User user : users)
{
String encryptedPassword = EncryptionUtil.decryptPassword(user.getPassword(), user.getSalt());
auth.inMemoryAuthentication().withUser(user.getUsername()).password(encryptedPassword).roles(user.getRole());
}
}
/**
* Provide custom system messages to make sure the application is reloaded when the session expires.
*/
#SuppressWarnings("serial")
#Bean
SystemMessagesProvider systemMessagesProvider()
{
return new SystemMessagesProvider()
{
#Override
public SystemMessages getSystemMessages(SystemMessagesInfo systemMessagesInfo)
{
CustomizedSystemMessages systemMessages = new CustomizedSystemMessages();
systemMessages.setSessionExpiredNotificationEnabled(false);
return systemMessages;
}
};
}
}
Now if the user did a login he has the option to edit his user account settings. This changes the user object in the database (e.g. the username for login). Now if he does a logout, i want the application to reload the userlist, so the user can use his new username. How is this possible?
Regards
shinchillahh

In short, replace your in-memory authentication with DAO authentication.
Please note that in the example below UserDetailsService userService is the Spring core interface, and UserRepository userRepository is the DAO for your users (aka UserService userService in your example).
1. Configuration
#Configuration
public class Authorization extends GlobalAuthenticationConfigurerAdapter {
#Autowired
private UserDetailsService userService;
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
2. Service providing user details
#Service
public class UserService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username);
}
return user;
}
}

Related

I am trying to implement spring security ive configured everything but still not working

spring security not using details from database for authentication and generating password in console also not using my customized login form.
Main class--
package mis.main;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication
#ComponentScan({"mis.controller", "mis.services"})
#EntityScan("mis.entity")
#EnableJpaRepositories("mis.dao")
public class BitmisApplication {
public static void main(String[] args) {
SpringApplication.run(BitmisApplication.class, args);
}
}
CustomUserDetails--
package mis.config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import mis.entity.Roles;
import mis.entity.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class CustomUserDetails implements UserDetails {
private User user;
public CustomUserDetails(User user) {
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<Roles> roles = user.getRoles();
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (Roles role : roles) {
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return authorities;
}
#Override
public String getPassword() {
return user.getPassword();
}
#Override
public String getUsername() {
return user.getUsername();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return user.isEnabled();
}
}
MyConfig class--
package mis.config;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
#Configuration
#EnableAutoConfiguration
#EnableWebSecurity
public class MyConfig extends WebSecurityConfigurerAdapter {
#Bean
public UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").hasAnyAuthority("USER", "CREATOR", "EDITOR", "ADMIN")
.antMatchers("/new").hasAnyAuthority("ADMIN", "CREATOR")
.antMatchers("/admin/**").hasAnyAuthority("ADMIN", "EDITOR")
.antMatchers("/delete/**").hasAuthority("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin().permitAll()
.and()
.logout().permitAll()
.and()
.exceptionHandling().accessDeniedPage("/403")
;
}
}
UserDetailsServiceImpl--
package mis.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import mis.entity.User;
import mis.dao.UserRepository;
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userRepository.getUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("Could not find user");
}
return new CustomUserDetails(user);
}
}
UserRepository--
" package mis.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import mis.entity.User;
public interface UserRepository extends JpaRepository<User, Long> {
#Query("SELECT u FROM User u WHERE u.username = :username")
public User getUserByUsername(#Param("username") String username);
}"
I think springboot is not able to read this authentication files
the spring application can't find your configs since your project structure is faulty and you have added faulty custom configuration.
the #SpringBootApplication class will per default scan the package it is in mis.main and all packages below this one (mis.main...* etc) in order to find all spring annotated classes and load them.
You have placed your configuration files in mis.config which is not directly below mis.main, and you have files in mis.entity which is also not beneath mis.main.
You have also added
#ComponentScan({"mis.controller", "mis.services"})
#EntityScan("mis.entity")
in order to try to find the configuration files but failed to specify mis.config as a folder to scan in.
The simplest solution would be to
remove to two annotations i mentioned above.
move the main class into the package mis and then delete the package mis.main so that the main class is at the project root.
some other things:
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
is not needed and can be removed, because you have declared a custom UserDetailsService and PasswordEncoder as beans, they will automatically be picked up by the system and included into spring and it will set up DaoAuthentication for you automatically.
If you are learning you should read the spring security reference docs all this is mentioned there.

Spring security application giving No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken

I am new to spring boot. I am trying to implement a simple spring boot security with userdetailsservice in Spring Tool Suite(STS).
Below is the controller I used:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class HomeController {
#GetMapping("/")
public String home() {
return("<h1>Welcome</h1>");
}
#GetMapping("/user")
public String user() {
return("<h1>Welcome user</h1>");
}
}
And the Web security configuration code:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
#EnableWebSecurity
public class AppSecureConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/user").hasRole("USER")
.antMatchers("/").permitAll()
.and().formLogin()
.and().logout().permitAll();
}
#Bean
public PasswordEncoder getPasswordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
I gave all the required dependencies in pom.xml.
So, I have added below line in application.propperties file, and now system is not generating security password.
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration
And I have included user details Service for credentials.
Below is user detail service class
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
#Service
public class MyuserDetails implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
return new userPrincipal(s);
}
}
and userPrincipal class
import java.util.Arrays;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class userPrincipal implements UserDetails {
private static final long serialVersionUID = 1L;
private String userName;
public userPrincipal(String userName) {
this.userName = userName;
}
public userPrincipal() {
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
}
#Override
public String getPassword() {
// TODO Auto-generated method stub
return "pass";
}
#Override
public String getUsername() {
// TODO Auto-generated method stub
return userName;
}
#Override
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return true;
}
#Override
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return true;
}
#Override
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return true;
}
#Override
public boolean isEnabled() {
// TODO Auto-generated method stub
return true;
}
}
now, when I ran the application using http://localhost:8081/ url, it is giving "No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken".
I am using Spring tool suite(STS) to run this project. Can some one point out what am I doing wrong here?
Do not exclude the entire SecurityAutoConfiguration, instead if you want you should just exclude the org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration.
Alternatively, you can expose a UserDetailsService bean that will do the same for you, and you can get rid of the configureGlobal method, like so:
#Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
Add this to your application.properties
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration
And your class will look like this:
Add #Configuration to the class (as suggested by M. Deinum)
Specify the role for the user, otherwise you will get java.lang.IllegalArgumentException: Cannot pass a null GrantedAuthority collection
#Configuration
#EnableWebSecurity
public class AppSecureConfig extends WebSecurityConfigurerAdapter {
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("{noop}"+"pass").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/user").hasRole("USER")
.antMatchers("/").permitAll()
.and().formLogin()
.and().logout().permitAll();
}
}

Spring security , Principal returns null

i am tryig to post an object to the database using postman , the token generated corectlly but the principal is null !? why
in the controller I have the following code .
and the error as shown
the principal tostring is : UsernamePasswordAuthenticationToken [Principal=com.accessjobs.pjp.domain.User#1a1e348b, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[]]
: the principal getName is : null
#RestController
#RequestMapping("/api/job")
#CrossOrigin //to hit the backend server when connect from the frontend server
public class JobController {
Logger logger = LoggerFactory.getLogger(JobController.class);
#Autowired
private JobService jobService;
#Autowired
private MapValidationErrorsService mapValidationErrorsService;
//#valid annotation to validate the object attributes and , i used the following code FieldErrors to display the list of errors from validation
#PostMapping("")
#CrossOrigin
public ResponseEntity<?> add(#Valid #RequestBody Job job, BindingResult result, Principal principal) {
ResponseEntity<?> errorMap = mapValidationErrorsService.MapValidationService(result);
if (errorMap != null) return errorMap;
logger.info("the principal tostring is : "+principal.toString());
logger.info("the principal getName is : "+principal.getName());
Job tempJob = jobService.add(job, principal.getName());
return new ResponseEntity<Job>(tempJob, HttpStatus.CREATED);
}
and the service job code as shown , always give me a null user and catch the error .
that because the email is null.
what is the solution>?
import com.accessjobs.pjp.domain.Job;
import com.accessjobs.pjp.domain.User;
import com.accessjobs.pjp.exceptions.JobIdException;
import com.accessjobs.pjp.repositories.JobRepository;
import com.accessjobs.pjp.repositories.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Locale;
#Service
public class JobService {
Logger logger = LoggerFactory.getLogger(JobService.class);
#Autowired
private JobRepository jobRepository;
#Autowired
private UserRepository userRepository;
public Job add(Job job,String email){
//logic , validation and handling
try {
logger.info("email .is"+email);
User user = userRepository.findByEmail(email);
logger.info("user is ::"+user.toString());
job.setUser(user);
logger.warn(user.getEmail());
job.setUserRole(user.getRole());
logger.info(user.getRole());
//convert the identifier to upper case
job.setJobIdentifier(job.getJobIdentifier().toUpperCase(Locale.ROOT));
return jobRepository.save(job);
}catch (Exception e){
throw new JobIdException("Job Identifier '"+job.getJobIdentifier().toUpperCase(Locale.ROOT)+"' already exists");
}
}
package com.accessjobs.pjp.services;
import com.accessjobs.pjp.domain.User;
import com.accessjobs.pjp.exceptions.EmailAlreadyExistsException;
import com.accessjobs.pjp.repositories.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
#Service
public class UserService {
#Autowired
private UserRepository userRepository;
#Autowired //define the bean in the main spring boot app
private BCryptPasswordEncoder bCryptPasswordEncoder;//store non readable password in the database
public User register(User newUser){
try{
newUser.setPassword(bCryptPasswordEncoder.encode(newUser.getPassword()));
//user name has to be unique (exception)
newUser.setEmail(newUser.getEmail());
//make sure that pass and confpass are matches
//we dont presist or show the confirm password
newUser.setConfirmPassword("");
return userRepository.save(newUser);
}catch (Exception e){
throw new EmailAlreadyExistsException("Email' "+newUser.getEmail()+"' is already exists");
}
}
}
maybe the error in the database , how can I permit all URLs for the MySQL database
this is the class that implements the user detailsservice
package com.accessjobs.pjp.services;
import com.accessjobs.pjp.domain.User;
import com.accessjobs.pjp.repositories.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userRepository.findByEmail(email);
if(user==null) new UsernameNotFoundException("User not found");
return user;
}
#Transactional
public User loadUserById(Long id){
User user = userRepository.getById(id);
if(user==null) new UsernameNotFoundException("User not found");
return user;
}
}
is there any errors in this class ?
especially in the MySQL permissions and antMatchers?
package com.accessjobs.pjp.security;
import com.accessjobs.pjp.services.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import static com.accessjobs.pjp.security.SecurityConstants.*;
//video rep branch 65
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(
securedEnabled = true,
jsr250Enabled = true,
prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(customUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Override
#Bean(BeanIds.AUTHENTICATION_MANAGER)
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// .and().headers().frameOptions().sameOrigin() //To enable H2 Database
.and()
.authorizeRequests()
.antMatchers(
"/",
"/favicon.ico",
"/**/*.png",
"/**/*.gif",
"/**/*.svg",
"/**/*.jpg",
"/**/*.html",
"/**/*.css",
"/**/*.js"
).permitAll()
.antMatchers("/api/users/**").permitAll()
.antMatchers(SIGN_UP_URLS).permitAll()
.antMatchers("jdbc:mysql://localhost:3306/").permitAll()**//here ?**
.antMatchers("/api/users/**").permitAll()
.antMatchers("/api/users/**").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
Make sure your token has a sub property. Use this tool to post the contents of your token (minus any secure properties).
https://jwt.io/

Springboot security config not redirecting to successUrl

I dont seem to understand what is wrong with my code. I'm trying to login using Springboot security, everything seems correct and I can see my username and password on console. Can anyone tell me what i'm getting wrong?
Here's my SecSecurityConfig class
package com.scanapp.config;
import com.scanapp.repositories.RoleRepository;
import com.scanapp.services.UserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import
org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.crypto.password.PasswordEncoder;
#Configuration
#EnableWebSecurity
public class SecSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private RoleRepository roleRepository;
#Autowired
#Qualifier("myuserdet")
UserDetailsService userDetailsService;
protected void init(AuthenticationManagerBuilder auth) throws Exception {
System.out.println("I'm here");
auth.authenticationProvider(authProvider());
}
#Bean
public DaoAuthenticationProvider authProvider() {
System.out.println("got here");
DaoAuthenticationProvider authProvider = new
DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
#Bean
public PasswordEncoder passwordEncoder()
{
return new CustomPassword();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/resources**").permitAll()
.anyRequest().permitAll()
.and().formLogin().loginPage("/login").successForwardUrl("/index").defaultSuccessUrl("/index",true).failureUrl("/login?error").permitAll()
.and()
.csrf().disable();
System.out.println("got here too");
}
}
UserServiceDetails.java
package com.scanapp.services;
import com.drew.metadata.StringValue;
import com.scanapp.config.MyUserPrincipal;
import com.scanapp.config.SecSecurityConfig;
import com.scanapp.models.User;
import com.scanapp.repositories.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Objects;
#Qualifier("myuserdet")
#Service
public class UserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByEmail(username);
if (Objects.nonNull(user)) {
MyUserPrincipal principal = new MyUserPrincipal(user);
System.out.println(String.valueOf(principal));
System.out.println("User Found");
System.out.println(principal.getPassword());
System.out.println(principal.getUsername());
return principal;
}else {
throw new BadCredentialsException("User Not found");
}
}
}
MyUserPrincipal.java
package com.scanapp.config;
import com.scanapp.models.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
public class MyUserPrincipal implements UserDetails {
private User user;
public MyUserPrincipal(User user) {
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
#Override
public String getPassword()
{
return user.getPassword();
}
#Override
public String getUsername()
{
return user.getEmail();
}
#Override
public boolean isAccountNonExpired() {
return false;
}
#Override
public boolean isAccountNonLocked() {
return false;
}
#Override
public boolean isCredentialsNonExpired() {
return false;
}
#Override
public boolean isEnabled() {
return false;
}
}
CustomPassword.java
package com.scanapp.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class CustomPassword extends BCryptPasswordEncoder {
Logger logger = LoggerFactory.getLogger(this.getClass());
#Override
public String encode(CharSequence rawPassword) {
return super.encode(rawPassword);
}
#Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
System.out.println("paasword etered {}" + rawPassword);
return super.matches(rawPassword, encodedPassword);
}
}
Try to remove this block from the configuration. In theory Spring creates all of these beans behind the scenes (picking up your passwordEncoder and UserDetailsService automatically).
#Autowired
#Qualifier("myuserdet")
UserDetailsService userDetailsService;
protected void init(AuthenticationManagerBuilder auth) throws Exception {
System.out.println("I'm here");
auth.authenticationProvider(authProvider());
}
#Bean
public DaoAuthenticationProvider authProvider() {
System.out.println("got here");
DaoAuthenticationProvider authProvider = new
DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
If it doesn't work try to rename your UserDetailsService (although that's a long shot).
You have a lot of noise in your code.
1.You defined CustomPassword which just extends BCryptPasswordEncoder. I suggest to return
#Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
2. You defined another User model which does nothing and has null list of granted authorities. It is odd, because it should fail if the list of authorities is null. Please return import
org.springframework.security.core.userdetails.User;
//..
return new User(userName, encodedPassword, Collections.singletonList(new SimpleGrantedAuthority("USER")
3.It is better to use another names for your beans, not the spring ones. please rename UserDetailsService to CustomUserDetailsService and don't use qualifiers in your configuration.
4.Please make sure that when you save the passwords in your database, they are hashed with BCryptPasswordEncoder.
OK. Another idea:
Based on the docs this method shouldn't return null:
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
return AuthorityUtils.NO_AUTHORITIES; instead.
In theory it could raise a NullpointerException during the authentication AFTER your UserDetailsService created the principal. And if there is a general catch (Exception e) somewhere there then it will be simply embedded into an AuthenticationException.
--- Edit
OUCH!
You should also change the return value to true in the last four method in the MyUserPrincipal class:
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
Your princapal was always disabled and expired and all. Of course it was not allowed to log in ! :)

Setup Stateless Authentication using Spring Boot

I am using latest version of Spring Boot and I am trying to setup StatelessAuthenticaion. So far the tutorials I've been reading are very vague and I am not sure what I am doing wrong. The tutorials I am using is...
http://technicalrex.com/2015/02/20/stateless-authentication-with-spring-security-and-jwt/
The problem with my setup is that it seems everything is running correctly except for the fact that TokenAuthenticationService::addAuthentication is never called so my token is never set and therefore it returns null when the TokenAuthenticationService::getAuthentication is called and therefore returns a 401 even when I successfully logged in (Because addAuthentication is never called to set the token in the header). I am trying to figure a way to add TokenAuthenticationService::addAuthentication but I find it quite difficult.
In the tutorial he adds something similar to WebSecurityConfig::UserDetailsService.userService into auth.userDetailsService().. The only problem I am getting with that is when I do so, it throws a CastingErrorException. It only works when I utilize UserDetailsService customUserDetailsService instead...
WebSecurityConfig
package app.config;
import app.repo.User.CustomUserDetailsService;
import app.security.RESTAuthenticationEntryPoint;
import app.security.RESTAuthenticationFailureHandler;
import app.security.RESTAuthenticationSuccessHandler;
import app.security.TokenAuthenticationService;
import app.security.filters.StatelessAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.sql.DataSource;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
#Order(2)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static PasswordEncoder encoder;
private final TokenAuthenticationService tokenAuthenticationService;
private final CustomUserDetailsService userService;
#Autowired
private UserDetailsService customUserDetailsService;
#Autowired
private RESTAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private RESTAuthenticationFailureHandler authenticationFailureHandler;
#Autowired
private RESTAuthenticationSuccessHandler authenticationSuccessHandler;
public WebSecurityConfig() {
this.userService = new CustomUserDetailsService();
tokenAuthenticationService = new TokenAuthenticationService("tooManySecrets", userService);
}
#Autowired
public void configureAuth(AuthenticationManagerBuilder auth,DataSource dataSource) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").authenticated();
http.csrf().disable();
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
http.formLogin().defaultSuccessUrl("/").successHandler(authenticationSuccessHandler);
http.formLogin().failureHandler(authenticationFailureHandler);
//This is ho
http.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService),
UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService);
}
#Bean
#Override
public CustomUserDetailsService userDetailsService() {
return userService;
}
#Bean
public TokenAuthenticationService tokenAuthenticationService() {
return tokenAuthenticationService;
}
}
The TokenAuthenticationService successfully calls the getAuthentication method but in the tutorials I read, there is no proper explanation on how addAuthentication is called
TokenAuthenticationService
package app.security;
import app.repo.User.CustomUserDetailsService;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TokenAuthenticationService {
private static final String AUTH_HEADER_NAME = "X-AUTH-TOKEN";
private final TokenHandler tokenHandler;
//This is called in my WebSecurityConfig() constructor
public TokenAuthenticationService(String secret, CustomUserDetailsService userService) {
tokenHandler = new TokenHandler(secret, userService);
}
public void addAuthentication(HttpServletResponse response, UserAuthentication authentication) {
final UserDetails user = authentication.getDetails();
response.addHeader(AUTH_HEADER_NAME, tokenHandler.createTokenForUser(user));
}
public Authentication getAuthentication(HttpServletRequest request) {
final String token = request.getHeader(AUTH_HEADER_NAME);
if (token != null) {
final UserDetails user = tokenHandler.parseUserFromToken(token);
if (user != null) {
return new UserAuthentication(user);
}
}
return null;
}
}
TokenHandler
package app.security;
import app.repo.User.CustomUserDetailsService;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.core.userdetails.UserDetails;
public final class TokenHandler {
private final String secret;
private final CustomUserDetailsService userService;
public TokenHandler(String secret, CustomUserDetailsService userService) {
this.secret = secret;
this.userService = userService;
}
public UserDetails parseUserFromToken(String token) {
String username = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody()
.getSubject();
return userService.loadUserByUsername(username);
}
public String createTokenForUser(UserDetails user) {
return Jwts.builder()
.setSubject(user.getUsername())
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
}
In my WebServiceConfig. I add the following
http.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService),
UsernamePasswordAuthenticationFilter.class);
Which calls on the following class as a filter. It gets the Authentication, but there is No where where it actually adds it.
StatelessAuthenticationFilter
package app.security.filters;
import app.security.TokenAuthenticationService;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* Created by anthonygordon on 11/17/15.
*/
public class StatelessAuthenticationFilter extends GenericFilterBean {
private final TokenAuthenticationService authenticationService;
public StatelessAuthenticationFilter(TokenAuthenticationService authenticationService) {
this.authenticationService = authenticationService;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
Authentication authentication = authenticationService.getAuthentication(httpRequest);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
SecurityContextHolder.getContext().setAuthentication(null);
}
}
The following class is what gets passed in the TokenAuthenticationService::addAuthentication
UserAuthentication
package app.security;
import app.repo.User.User;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
public class UserAuthentication implements Authentication {
private final UserDetails user;
private boolean authenticated = true;
public UserAuthentication(UserDetails user) {
this.user = user;
}
#Override
public String getName() {
return user.getUsername();
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities();
}
#Override
public Object getCredentials() {
return user.getPassword();
}
#Override
public UserDetails getDetails() {
return user;
}
#Override
public Object getPrincipal() {
return user.getUsername();
}
#Override
public boolean isAuthenticated() {
return authenticated;
}
#Override
public void setAuthenticated(boolean authenticated) {
this.authenticated = authenticated;
}
}
Thats it...
My Solution (But Need Help)...
My solution was to set the TokenAuthenticationService::addAuthentication method in my success handler... The only problem with that is the tutorial added the class TokenAuthenticationService to the WebServiceConfig class. And thats the only place its accessible. If there is a way I can obtain it in my successHandler, I might be able to set the token.
package app.security;
import app.controllers.Requests.TriviaResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created by anthonygordon on 11/12/15.
*/
#Component
public class RESTAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
TriviaResponse tresponse = new TriviaResponse();
tresponse.setMessage("You have successfully logged in");
String json = ow.writeValueAsString(tresponse);
response.getWriter().write(json);
clearAuthenticationAttributes(request);
}
}
You have to call TokenAuthenticationService.addAuthentication() yourself when a user supplies their login credentials the first time.
The tutorial calls addAuthentication() in GoogleAuthorizationResponseServlet after a user successfully logs in using their Google account. Here's the relevant code:
private String establishUserAndLogin(HttpServletResponse response, String email) {
// Find user, create if necessary
User user;
try {
user = userService.loadUserByUsername(email);
} catch (UsernameNotFoundException e) {
user = new User(email, UUID.randomUUID().toString(), Sets.<GrantedAuthority>newHashSet());
userService.addUser(user);
}
// Login that user
UserAuthentication authentication = new UserAuthentication(user);
return tokenAuthenticationService.addAuthentication(response, authentication);
}
If you already have an authentication success handler, then I think you're on the right track that you need to call TokenAuthenticationService.addAuthentication() from there. Inject the tokenAuthenticationService bean into your handler and then start using it. If your success handler doesn't end up being a Spring bean then you can explicitly look tokenAuthenticationService up by calling WebApplicationContextUtils.getRequiredWebApplicationContext.getBean(TokenAuthenticationService.class).
There is also an issue in the tutorial's GitHub repo that will address the confusion between the initial login supplied by the user and the stateless authentication happening on all subsequent requests.
you can define a StatelessLoginFilter like below
.addFilterBefore(
new StatelessLoginFilter("/api/signin",
tokenAuthenticationService, userDetailsService,
authenticationManager()),
UsernamePasswordAuthenticationFilter.class)
and write the class like this
class StatelessLoginFilter extends AbstractAuthenticationProcessingFilter {
private final TokenAuthenticationService tokenAuthenticationService;
private final UserDetailsService userDetailsService;
protected StatelessLoginFilter(String urlMapping,
TokenAuthenticationService tokenAuthenticationService,
UserDetailsService userDetailsService,
AuthenticationManager authManager) {
super(new AntPathRequestMatcher(urlMapping));
this.userDetailsService = userDetailsService;
this.tokenAuthenticationService = tokenAuthenticationService;
setAuthenticationManager(authManager);
}
#Override
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain,
Authentication authentication) throws IOException, ServletException {
final User authenticatedUser = userDetailsService
.loadUserByUsername(authentication.getName());
final UserAuthentication userAuthentication = new UserAuthentication(
authenticatedUser);
tokenAuthenticationService.addAuthentication(response,
userAuthentication);
SecurityContextHolder.getContext()
.setAuthentication(userAuthentication);
}
}

Categories

Resources