how to solve bad credentials spring Security? - java

I can't log in to my app as a user with the role admin or as a user with the role user. I always log in as a user with the role anonymous.
Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public PasswordEncoder encoder() {
return new StandardPasswordEncoder("53cr3t");
}
#Autowired
UserDetailsServiceImpl userDetailsService;
public SecurityConfig(UserDetailsServiceImpl userDetailsService) {
this.userDetailsService = userDetailsService;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Bean
public DaoAuthenticationProvider authProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/").authenticated()
.antMatchers("/rentAppPage/").hasRole("ADMIN")
.antMatchers("/addVehicle").hasRole("ADMIN")
.antMatchers("/getVehicle").hasRole("ADMIN")
.antMatchers("/removeVehicle").hasRole("ADMIN")
.antMatchers("/updateVehicle").hasRole("ADMIN")
.antMatchers("/allUser").hasRole("ADMIN")
.antMatchers("/resultGet").hasRole("ADMIN")
.antMatchers("/addUser").hasRole("ADMIN")
.antMatchers("/getUser").hasRole("ADMIN")
.antMatchers("/updateUser").hasRole("ADMIN")
.antMatchers("/removeUserById").hasRole("ADMIN")
.antMatchers("/price").hasAnyAuthority("ROLE_ADMIN", "ROLE_USER")
.antMatchers("/allScooter").hasAnyAuthority( "ROLE_ADMIN", "ROLE_USER")
.antMatchers("/allCar").hasAnyAuthority( "ROLE_ADMIN", "ROLE_USER")
.antMatchers("/allMotorBike").hasAnyAuthority( "ROLE_ADMIN", "ROLE_USER")
.antMatchers("/allBike").hasAnyAuthority("ROLE_USER", "ROLE_ADMIN")
.antMatchers("/distance").hasAnyAuthority("ROLE_USER", "ROLE_ADMIN")
.antMatchers("/user").hasAnyAuthority( "ROLE_ADMIN", "ROLE_USER")
.antMatchers("/rent").hasAnyAuthority( "ROLE_ADMIN", "ROLE_USER")
.antMatchers("/rent2").hasAnyAuthority( "ROLE_ADMIN", "ROLE_USER")
.antMatchers("/buy").hasAnyAuthority( "ROLE_ADMIN", "ROLE_USER")
.antMatchers("/buy2").hasAnyAuthority( "ROLE_ADMIN", "ROLE_USER")
.antMatchers("/thanks").hasAnyAuthority( "ROLE_ADMIN", "ROLE_USER")
.antMatchers("/rentAppPage").hasAnyAuthority( "ROLE_ADMIN", "ROLE_USER")
.and()
.formLogin()
.defaultSuccessUrl("/", true)
.and()
.logout()
.logoutSuccessUrl("/");
;
http.sessionManagement()
//.expiredUrl("/sessionExpired.html")
.invalidSessionUrl("/login.html");
}
}
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int idUser;
private String username;
private String password;
private String name;
private String surname;
private String email;
private double latitude;
private double longitude;
private String role;
private String locationName;
}
#Slf4j
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
IUserDAO userDAO;
public UserDetailsServiceImpl(IUserDAO userDAO){
this.userDAO = userDAO;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDAO.findByUsername(username);
if(user == null){
throw new UsernameNotFoundException("User dont exist");
}
return new MyUserDetails(user);
}
}
#Repository
public class UserDAOImpl implements IUserDAO {
#Autowired
SessionFactory sessionFactory;
public UserDAOImpl(SessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
#Override
public void addUser(User user){
Session session = null;
Transaction tx = null;
try{
session = this.sessionFactory.openSession();
tx = session.beginTransaction();
session.save(user);
tx.commit();
}catch (HibernateException e){
if(tx != null)tx.rollback();
}finally {
session.close();
}
}
#Override
public User findByUsername(String username) {
Session session = this.sessionFactory.openSession();
User user =(User) session.createQuery("FROM pl.edu.wszib.model.User WHERE username = :username" )
.setParameter("username", username)
.uniqueResult();
session.close();
return user;
}
}
public class MyUserDetails implements UserDetails {
private User user;
public MyUserDetails(User user) {
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(user.getRole());
return Arrays.asList(authority);
}
#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 true;
}
}
2021-01-18 17:05:43.545 DEBUG 4256 --- [io-8080-exec-10] o.s.s.w.a.AnonymousAuthenticationFilter : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken#cd98cfcc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#0: RemoteIpAddress: 127.0.0.1; SessionId: AD82C9600EFB66CF7C6F8A1BCCEEAE0D; Granted Authorities: ROLE_ANONYMOUS'
i have databse in MySQL with two users first role admin second role user my role in database is ROLE_ADMIN,ROLE_USER i my full code is here https://github.com/Conrado1212/SpringSecurityWhyCantWorkFine
can someone explain why i cant log in to app as user with role admin or user ?

Of course, there are many questions about encoder, md5, etc. But it's your choice even it's wrong )))
I run your code, if MD5 hash is correct in DB I can login. As I understand, the question is why the application does not store the state of logged in user?
Expectation:
1st request: call method (incognito) -> login -> call method (authorized)
Xth request: call method -> Spring Security checks -> call method (authorized)
Reality:
Xth request: always equals 1st
The cause that you don't change a state of Spring Security context. You call /login endpoint, check if the User is in DB, check his password and return success response. You should create an authorization and place it in the Spring Security context.
Simple example:
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(username, password);
Authentication authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
String token = tokenProvider.generateToken(authentication);
return token;
And in next requests use the token. How to implement the token provider there are many examples. It can be custom or better JWT.
If you don't want tokens (RESTful), you can use sessions in requests (RESTless) but it requires additional configuration.

Looked through the code you have posted on github and none of the code you posted in the above question is the problem. You problem is how you perform your login.
login.html
<form action="/authenticate" method="post" id="login" class="input-group">
<div th:text="`enter code here`${errorMessage}" id="error-message"></div>
<input type="text" class="input-field" placeholder="Enter username" name="username" th:field="*{userModel.username}" required>
<input type="password" class="input-field" placeholder="Enter password" name="password" th:field="*{userModel.password}"required>
<input type="checkbox" class="chech-box"><span class="span1">Remember password</span>
<button type="submit" class="submit-btn">Log in</button>
</form>
Here we se that the login form posts to the endpoint /authenticate. This is NOT the standard /login endpoint that spring security sets up for you automatically.
Since you are not using the the standard i find your custom endpoint.
LoginController.java
#RequestMapping(value = "/authenticate",method = RequestMethod.POST)
public String authenticateUser(#ModelAttribute("userModel")User user,Model model){
boolean authResult = this.authenticationService.authenticationUser(user);
if(authResult){
System.out.println("zalogowano !!");
return "rentAppPage";
} else {
model.addAttribute("errorMessage","zle dane!!!");
model.addAttribute("userModel",new User());
return "login";
}
}
Here we see that you pass the user object into a custom written function called authenticationUser. So if we look in that function we find this implementation.
AuthenticationService.java
#Override
public boolean authenticationUser(User user){
User userFromDb = this.userDAO.findByUsername(user.getUsername());
return userFromDb != null && userFromDb.getPassword().equals(DigestUtils.md5Hex(user.getPassword()));
}
All you do in this function, is to fetch the user from the database. Check if the users password matches, and then return a boolean.
If the boolean is true you return the next page.
This is NOT how spring security works. All of the above code is completely faulty, and its clear that no research has been done before asking here on stack overflow. How could the server know you have called that function before? it can't.
My answer to your question, is that your implementation is completely wrong, and i highly suggest you find a getting started guide to how form login works in spring security, because explaining how spring security works, can not be done in a simple answer.
All im going to do is to link you to the official Spring security FormLogin documentation, and you should start reading there.

Related

Spring Security - 403 Forbidden for every role except ROLE_USER

I am creating an application using Role based restriction to certain REST endpoints.
I have a Roles table, user_roles table and a users table.
ROLE_USER is working fine, and users with that Role can successfully access their pages. Even if I swap it around so that only ROLE_USER can access /admin/**, it works.
However, when I try use ROLE_ADMIN on the admin page, it never works. It always redirects me to the 403 Forbidden page
Security Config:
#Bean
DaoAuthenticationProvider provider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsServiceImpl);
provider.setPasswordEncoder(passwordEncoder);
return provider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(provider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/banking/**").hasAnyRole("USER", "ADMIN")
.antMatchers(HttpMethod.GET, "/admin/**").hasRole("ADMIN")
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.logout().permitAll()
.and()
.exceptionHandling().accessDeniedPage("/403");
}
}
MyUserDetails:
public class MyUserDetails implements UserDetails {
private static final long serialVersionUID = -2067381432706760538L;
private User user;
public MyUserDetails(User user) {
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<Role> roles = user.getRoles();
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (Role role : roles) {
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));
}
return authorities;
}
UserDetailsServiceImpl:
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserService userService;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.findByUsername(username);
if(user == null) {
throw new UsernameNotFoundException("User not found");
}
return new MyUserDetails(user);
}
}
Would appreciate any help as I've been stuck on this for a while. Thanks!

Spring Security: issues 403 after authorization with single granted

Used Spring Boot 2 + Spring Security Starter.
Authorizes users, but for some reason gives an error 403.
I tried to configure in different ways, but it does not work.
After successful authorization (the loadUserByUsername method works fine) it shows 403 on all pages with the / admin prefix, and before authorization, switching to any page with this prefix leads to a redirect to / login
#Controller
public class AdminController {
#RequestMapping(value = "/admin", method = {GET, POST})
public String adminMainPage() {
return "redirect:/admin/article";
}
}
#Controller
#RequestMapping("/admin/article")
public class ArticleController {
#RequestMapping(value = "", method = {GET, POST})
public ModelAndView indexAdminPage(...){
...
}
}
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements UserDetailsService {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.userDetailsService(this)
.authorizeRequests()
.antMatchers("/", "/login",
"/login*", "/assets/**", "/lib/**", "/page.scripts/*").permitAll()
.antMatchers("/admin/**").hasAnyRole("ADMIN")
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.usernameParameter("login")
.passwordParameter("password")
.successForwardUrl("/admin")
.permitAll()
.and()
.logout()
.deleteCookies("JSESSIONID")
.permitAll();
}
private Collection<? extends GrantedAuthority> adminGrantedAuthoritySet = new HashSet<>() {{
add(new SimpleGrantedAuthority("ADMIN"));
}};
private final UserRepository userRepository;
public WebSecurityConfig(UserRepository userRepository ) {
this.userRepository = userRepository;
}
#Override
public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {
Optional<UserEntity> optionalUser = userRepository.findByLogin(login);
if (optionalUser.isEmpty()) {
throw new UsernameNotFoundException("User by login '" + login + "' not found");
} else {
UserEntity userEntity = optionalUser.get();
return new User(login, userEntity.getPassword(), adminGrantedAuthoritySet);
}
}
}
In Spring Security there is a distinction between a role and an authority.
A role is an authority that is prefixed with "ROLE_". In this example the authority "ROLE_ADMIN" is the same as the role "ADMIN".
You are setting your admin authorities to be a list of new SimpleGrantedAuthority("ADMIN"), but you are restricting access to .hasAnyRole("ADMIN").
You need to change one of those configurations.
If you use .hasAnyRole("ADMIN"), then you should change the admin authorities list to use new SimpleGrantedAuthority("ROLE_ADMIN").
Otherwise, if you want your list to be new SimpleGrantedAuthority("ADMIN"), then you should use .hasAnyAuthority("ADMIN").
First, I will advice that you separate UserDetailsService from the WebSecurityConfig.
Have a separate class for UserDetailsService like
#Service("customCustomerDetailsService")
public class CustomCustomerDetailsService implements UserDetailsService {
#Autowired
private CustomerRepository customers;
#Override
public UserDetails loadUserByUsername(String email) {
return this.customers.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException("Username: " + email + " not found"));
}
}
Then your UserEntity should implement UserDetails class where you set the authorities.See the answer //userdetails
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.roles.stream().map(SimpleGrantedAuthority::new).collect(toList());
}
#Override
public String getUsername() {
return this.getEmail();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
#Transient
private List<String> roles = Arrays.asList("ROLE_USER");
public List<String> getRoles() {
return roles;
}
Then you need DAOauthentication manager which makes use of the UserDetailsService like this:
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
#Bean
#Override
public UserDetailsService userDetailsService() {
return new CustomCustomerDetailsService();
}
I don't know think putting everything in the WebSecurityConfig is good practice and it will be complicated and prone to errors!

Adding a custom filter to authentication process in Spring Security

Right now my authentication is done with username and password. I'd like to add one more step to it so that it checks if user is activated or not. I have a user table that holds the value if the user has activated the account.
I have my SecurityConfig.java
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// This somehow works but only if the account is not validated
// auth.authenticationProvider(new UserActivatedAuthenticationProvider(userService));
auth.userDetailsService(userDetailsService).passwordEncoder(new ShaPasswordEncoder(encodingStrength));
}
And UserActivatedAuthenticationProvider.java
#Component
public class UserActivatedAuthenticationProvider implements AuthenticationProvider {
private final UserService userService;
#Autowired public UserActivatedAuthenticationProvider(UserService userService) {
this.userService = userService;
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String name = authentication.getName();
User user = userService.findByEmail(name);
if (user != null) {
if (!user.isActivated()) {
throw new BadCredentialsException(name + " email is not activated.");
}
}
Object credentials = authentication.getCredentials();
if (!(credentials instanceof String)) {
return null;
}
String password = credentials.toString();
Authentication auth = new UsernamePasswordAuthenticationToken(name, password);
return auth;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
I want to proceed with authentication only if the account is activated. I can't use userService in AuthenticationManagerBuilder because I can't get the username. I am using this project as a seed. In short... I also want to check the value of is_activated column and proceed based on that value as it now does (username and password validation).
You don't need an AuthenticationProvider. You need to implement the UserDetailsService as following;
#Service
public class MyUserDetailsService implements UserDetailsService {
#Autowired
private UserService userService;
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userService.findByEmail(username);
if(user == null) {
throw new UsernameNotFoundException(username);
}
return org.springframework.security.core.userdetails.User(username, user.getPassword(), user.isActivated(), true, true, true, user.getRoles().stream().map(role -> role.getRoleName()).map(SimpleGrantedAuthority::new).collect(Collectors.toList()));
}
}
The spring class org.springframework.security.core.userdetails.User as an property named enabled for which you can pass your user.isActivated() flag from database.
You can do that by providing your custom user details service.
#Autowired
private CustomUserDetailsService customUserDetailsService ;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService ).passwordEncoder(new ShaPasswordEncoder(encodingStrength));
}
and
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserService userService;
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userService.findByEmail(username);
if(user == null) {
throw new UsernameNotFoundException(username);
}
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("USER");
authorities.add(grantedAuthority);
return org.springframework.security.core.userdetails.User(username, user.getPassword(), user.isActivated(), true, true, true, authorities );
}
}
Now based on Boolean value of third param, spring security will auto allow/deny user login and will also give the message "User disabled" if user is not activated.

Spring Security custom UserDetailsService and custom User class

I am trying to save additional data in de user principal object.
What i did was:
implement the "UserDetails" interface to my existing user class where my additional data is saved ( like email address etc. ).
#Entity
public class User implements UserDetails {
Then i created a UserDetailsService implementation:
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
UserDAO userDAO;
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userDAO.findOneByUsername(username);
if (user == null)
throw new UsernameNotFoundException("username " + username
+ " not found");
System.out.println("---------------------> FOUND ------------->"
+ user.getEmail());
return user;
}
}
Last step was to add the UserDetailsService in my Security configuration.
#Configuration
#EnableWebMvcSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userDetailsService());
// ...
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.userDetailsService(userDetailsService());
// ...
}
#Override
protected UserDetailsService userDetailsService() {
return userDetailsService;
}
I see in my console that "loadUserByName" gets called twice ( because of the "Found" output ).
When i try to access the principal object in my controller ->
System.out.println(SecurityContextHolder.getContext()
.getAuthentication().getPrincipal());
I dont get my additional data.
When i try to cast it to my User object i get a could not cast exception.
Is there anything I am missing??
Thank you in advance.
Ok. My problem was hidden in the code i didnt post.
I thought this detailsService is only to get additional details but it is used for the login itself.
I had "jdbcAuthentication" configured additionally and spring seemed to use this always.
Now that i only got the detailsService configured everything works fine.
edit.:
so i only had to delete this code:
auth.jdbcAuthentication() .dataSource(dataSource)
* .passwordEncoder(passwordEncoder) .usersByUsernameQuery(
// ....
And now it also works with my code in the question above.
Create Extention class:
public class CustomUserDetails extends org.springframework.security.core.userdetails.User{
private User user;
public CustomUserDetails(User user, Collection<? extends GrantedAuthority> authorities) {
super(user.getName(), user.getPassword(), authorities);
this.user = user;
}
public CustomUserDetails(User user, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
super(user.getName(), user.getPassword(), enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
this.user = user;
}
public User getUser() {
return user;
}
}
Than add it to UserDetailsService:
#Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException, DataAccessException {
UserDetails userDetails = null;
User user = userService.getByLogin(login);
userDetails = new CustomUserDetails(user,
true, true, true, true,
getAuthorities(user.getRole()));
return userDetails;
}
Get it!
(CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()

Spring Security - Angular JS - Token based authentication with custom GenericFIlterBean

I got stuck with a problem regarding token based authentication using the spring security framework.
What Iam trying to achieve is to authenticate an angular js client with a webservice based on spring.
When the client logs in, I am generating a token which is passed along with every request made to the back end. So far this is working quite well.
The token is generated and returned to the client successfully. Passing the token for each request is also working. My problem is that my custom filter is authenticating the client successfully, but after going through the rest of the filter chain it is always returning a HTTP 403 and I have no clue where it is coming from.
The code is inspired by https://github.com/philipsorst/angular-rest-springsecurity and I tried to integrate it into my own app.
So here is my set up:
Java Spring Config Application.java:
#Configuration
#ComponentScan
#EnableAutoConfiguration
#ImportResource("classpath:applicationContext.xml")
#Order(1)
public class Application {
static Logger log = Logger.getLogger(Application.class.getName());
private static ConfigurableApplicationContext context;
public static void main(String[] args) {
context = SpringApplication.run(Application.class);
DBUtil dbUtil = context.getBean(DBUtil.class);
dbUtil.insertDestData();
}
}
#EnableWebMvcSecurity
#EnableWebSecurity(debug = true)
#ComponentScan
#Configuration
#EnableAutoConfiguration
#Order(2)
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private ConfigurableApplicationContext context;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(
SessionCreationPolicy.STATELESS);
String[] restEndpointsToSecure = { "api/recipe", "api/ingredient" };
//for (String endpoint : restEndpointsToSecure) {
http.authorizeRequests().antMatchers("/api/ingredient/create/")
.hasRole("USER");
//}
SecurityConfigurer<DefaultSecurityFilterChain, HttpSecurity> securityConfigurerAdapter = new XAuthTokenConfigurer(
userDetailsServiceBean(), authenticationManagerBean());
http.apply(securityConfigurerAdapter);
}
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder)
throws Exception {
UserDAO userDAO = context.getBean(UserDAO.class);
authManagerBuilder.userDetailsService(userDAO);
}
#Bean(name = "myAuthManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
The user entity implementing the UserDetail interface
#Entity
#Table(name = "RB_USER")
public class User extends BaseAuditEntity implements UserDetails {
/**
*
*/
private static final long serialVersionUID = 3719799602561353931L;
public User() {
super();
}
#Column(name = "NAME")
private String userName;
#Column(name = "PASSWORD")
private String password;
#Column(name = "SECTOKEN")
private String secToken;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
private List<UserRole> userRoles;
#Column(name = "IS_ACCOUNT_NON_LOCKED")
private boolean isAccountNonLocked;
#Column(name = "IS_ENABLED")
private boolean isEnabled;
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<String> roles = new HashSet<String>();
for (UserRole userRole : getUserRoles()) {
roles.add(userRole.getRole().getName());
}
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return userName;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return isAccountNonLocked;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return isEnabled;
}
public String getSecToken() {
return secToken;
}
public void setSecToken(String secToken) {
this.secToken = secToken;
}
public List<UserRole> getUserRoles() {
return userRoles;
}
public void setUserRole(List<UserRole> userRoles) {
this.userRoles = userRoles;
}
public void setUserName(String name) {
this.userName = name;
}
public void setPassword(String password) {
this.password = password;
}
public void setEnabled(boolean isEnabled) {
this.isEnabled = isEnabled;
}
public void setAccountNonLocked(boolean isAccountNonLocked) {
this.isAccountNonLocked = isAccountNonLocked;
}
}
The user dao implementing the user detail service interface
#Repository
#Transactional
public class UserDAOImpl extends GenericDAOImpl<User> implements UserDAO,
UserDetailsService {
public UserDAOImpl() {
super();
setClazz(User.class);
}
#Override
public User findByUserName(String userName) {
String queryString = "SELECT user FROM User user "
+ "LEFT JOIN FETCH user.userRoles userRoles "
+ "WHERE user.userName like ?1";
Query query = createQuery(queryString);
query.setParameter(1, userName);
return (User) query.getSingleResult();
}
#Override
public UserDetails loadUserByUsername(String username) {
String queryString = "SELECT user FROM User user "
+ "LEFT JOIN FETCH user.userRoles userRoles "
+ "WHERE user.userName like ?1";
Query query = createQuery(queryString);
query.setParameter(1, username);
return (User) query.getSingleResult();
}
}
My extended SecurityConfigurerAdapter applying the custom filter to the filter chain
public class XAuthTokenConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private UserDetailsService detailsService;
private AuthenticationManager authenticationManager;
public XAuthTokenConfigurer(UserDetailsService detailsService, AuthenticationManager authenticationManager) {
this.detailsService = detailsService;
this.authenticationManager = authenticationManager;
}
#Override
public void configure(HttpSecurity http) throws Exception {
XAuthTokenFilter customFilter = new XAuthTokenFilter(detailsService, authenticationManager);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
}
And what is basically the important part the filter itself
public class XAuthTokenFilter extends GenericFilterBean {
protected Logger log = Logger.getLogger(this.getClass().getName());
private final UserDetailsService detailsService;
private final TokenUtils tokenUtils = new TokenUtils();
private final AuthenticationManager authenticationManager;
private String xAuthTokenHeaderName = "x-auth-token";
public XAuthTokenFilter(UserDetailsService userDetailsService, AuthenticationManager authenticationManager) {
this.detailsService = userDetailsService;
this.authenticationManager = authenticationManager;
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
try {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String authToken = httpServletRequest
.getHeader(this.xAuthTokenHeaderName);
if (StringUtils.hasText(authToken)) {
String username = this.tokenUtils
.getUserNameFromToken(authToken);
UserDetails details = this.detailsService
.loadUserByUsername(username);
if (this.tokenUtils.validateToken(authToken, details)) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
details.getUsername(), details.getPassword(),
details.getAuthorities());
token.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
Authentication authentication = this.authenticationManager
.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
log.info("========================> " + SecurityContextHolder.getContext().getAuthentication().getName() + " , " + SecurityContextHolder.getContext().getAuthentication().isAuthenticated());
}
}
filterChain.doFilter(servletRequest, servletResponse);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
As I mentioned above the login is working perfectly as the back end is returning a token and the angular js app is applying the token to each request.
Now when I am for example trying to add a new ingredient which is for the moment the only endpoint I am securing (for test purposes) I always get a 403 HTTP error.
Request
Accept application/json, text/plain, */*
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Content-Length 15
Content-Type application/json;charset=utf-8
Cookie user=%7B%22id%22%3A1%2C%22version%22%3A0%2C%22createdAt%22%3A1402390667294%2C%22createdBy%22%3Anull%2C%22updatedAt%22%3Anull%2C%22updatedBy%22%3Anull%2C%22password%22%3A%22123%22%2C%22secToken%22%3A%22Vincent%3A1402394417353%3A62c641b65418ceecb47aad47d5fc5378%22%2C%22userRoles%22%3A%5B%7B%22id%22%3A1%2C%22version%22%3A0%2C%22createdAt%22%3A1402390667299%2C%22createdBy%22%3Anull%2C%22updatedAt%22%3Anull%2C%22updatedBy%22%3Anull%2C%22role%22%3A%7B%22id%22%3A1%2C%22version%22%3A0%2C%22createdAt%22%3A1402390667287%2C%22createdBy%22%3Anull%2C%22updatedAt%22%3Anull%2C%22updatedBy%22%3Anull%2C%22name%22%3A%22USER%22%7D%7D%5D%2C%22authorities%22%3A%5B%7B%22authority%22%3A%22USER%22%7D%5D%2C%22accountNonLocked%22%3Atrue%2C%22accountNonExpired%22%3Atrue%2C%22credentialsNonExpired%22%3Atrue%2C%22enabled%22%3Atrue%2C%22username%22%3A%22Vincent%22%7D
Host localhost:8080
Referer http://localhost:8080/
User-Agent Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:29.0) Gecko/20100101 Firefox/29.0
x-auth-token Vincent:1402394417353:62c641b65418ceecb47aad47d5fc5378
As you can see the token is passed along as expected and the log output states the the user Vincent was authenticated successfully
2014-06-10 11:00:56.201 INFO 4495 --- [nio-8080-exec-4] c.receiptbook.security.XAuthTokenFilter : ========================> Vincent , true
Now when the filter chain gets processed it is returning a 403 error in the end saying the resource cant be accessed
POST /api/ingredient/create/
403 Forbidden
localhost:8080
So I really appreciate any hint or tip to solve this problem, cause I am really running out of ideas how to track down this issue.
Regards,
Vincent
UPDATE:
While debugging the filter chain I came across the fact that the UsernamePasswordAuthenticationFilter is not even in the chain ...? The ExceptionTranslationFilter seems to react with a 403 HTTP error, but from the filter chain I am not able to say why he's reacting like that.
[0] WebAsyncManagerIntegrationFilter (id=75)
[1] SecurityContextPersistenceFilter (id=74)
[2] HeaderWriterFilter (id=71)
[3] LogoutFilter (id=83)
[4] XAuthTokenFilter (id=70)
[5] RequestCacheAwareFilter (id=116)
[6] SecurityContextHolderAwareRequestFilter (id=127)
[7] AnonymousAuthenticationFilter (id=130)
[8] SessionManagementFilter (id=133)
[9] ExceptionTranslationFilter (id=134)
[10] FilterSecurityInterceptor (id=138)
RESOLVED:
So in the end and after a few ours of debugging I finally resolved the issue. The only problem was my role naming. My roles we're just named "USER" or "ADMIN", but spring security expects the names to have the format "ROLE_USER" or "ROLE_ADMIN". This is for sure configurable, but also pretty bad documented.
Anyways I hope that this information could be usefull for someone else.

Categories