I am trying to allow user with certain role to access a department controller of my Spring boot app. But getting a 403 error on all user roles. I know that I am missing something, but don't have any idea what. Here is what I have so far:
WebSecurityConfiguration:
package com.example;
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.AuthenticationManager;
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
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Qualifier("userDetailsServiceImpl")
@Autowired
private UserDetailsService userDetailsService;
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**", "/registration").permitAll()
.antMatchers("/department/list").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
@Bean
public AuthenticationManager customAuthenticationManager() throws Exception {
return authenticationManager();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth,AuthenticationManagerBuilder auth2) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
}
My Users model is as follows
package com.example.model;
import javax.persistence.*;
import java.util.Set;
@Entity
@Table(name = "users")
public class Users {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String username;
private String password;
@Transient
private String passwordconfirm;
@ManyToMany
private Set<Roles> roles;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getpasswordconfirm() {
return passwordconfirm;
}
public void setpasswordconfirm(String passwordconfirm) {
this.passwordconfirm = passwordconfirm;
}
public Set<Roles> getRoles() {
return roles;
}
public void setRoles(Set<Roles> roles) {
this.roles = roles;
}
}
And the roles model:
package com.example.model;
import javax.persistence.*;
import java.util.Set;
@Entity
@Table(name = "roles")
public class Roles {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@ManyToMany(mappedBy = "roles")
private Set<Users> users;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Users> getUsers() {
return users;
}
public void setUsers(Set<Users> users) {
this.users = users;
}
}
And here is my UsersRepository:
package com.example.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.model.Users;
public interface UsersRepository extends JpaRepository<Users, Integer> {
Users findByUsername(String username);
}
RolesRepository:
package com.example.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.model.Roles;
public interface RolesRepository extends JpaRepository<Roles, Integer> {
}
My SecurityServiceImplementation is as follows:
package com.example.services;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
@Service
public class SecurityServiceImplementation implements SecurityService {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
private static final Logger logger =
LoggerFactory.getLogger(SecurityServiceImplementation.class);
@Override
public String findLoggedInUsername() {
Object userDetails =
SecurityContextHolder.getContext().getAuthentication().getDetails();
if (userDetails instanceof UserDetails) {
return ((UserDetails)userDetails).getUsername();
}
return null;
}
@Override
public void autoLogin(String username, String password) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new
UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
authenticationManager.authenticate(usernamePasswordAuthenticationToken);
if (usernamePasswordAuthenticationToken.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
logger.debug(String.format("Auto login %s successfully!", username));
}
}
}
And My userDetailsService is as follows
package com.example.services;
import com.example.model.Users;
import com.example.model.Roles;
import com.example.repository.UsersRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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;
import java.util.HashSet;
import java.util.Set;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UsersRepository usersRepository;
@Override
@Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) {
Users user = usersRepository.findByUsername(username);
if (user == null) throw new UsernameNotFoundException(username);
Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
for (Roles roles : user.getRoles()){
grantedAuthorities.add(new SimpleGrantedAuthority(roles.getName()));
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities);
}
}
My UserServiceImplementation:
package com.example.services;
import com.example.model.Users;
import com.example.repository.UsersRepository;
import com.example.repository.RolesRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.HashSet;
@Service
public class UsersServiceImplementation implements UsersService {
@Autowired
private UsersRepository usersRepository;
@Autowired
private RolesRepository rolesRepository;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Override
public void save(Users user) {
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
user.setRoles(new HashSet<>(rolesRepository.findAll()));
usersRepository.save(user);
}
@Override
public Users findByUsername(String username) {
return usersRepository.findByUsername(username);
}
}
Finaly my controller is as follows:
package com.example.controller;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
//import org.springframework.web.bind.annotation.RequestMapping;
//import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import com.example.model.Users;
import com.example.services.SecurityService;
import com.example.services.UsersService;
import com.example.validators.UsersValidator;
import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
@Controller
@SpringBootApplication
public class IndexController {
@Autowired
private UsersService usersService;
@Autowired
private SecurityService securityService;
@Autowired
private UsersValidator usersValidator;
@GetMapping("/registration")
public String registration(Model model) {
model.addAttribute("userForm", new Users());
return "registration";
}
@PostMapping("/registration")
public String registration(@ModelAttribute("userForm") Users userForm, BindingResult
bindingResult) {
usersValidator.validate(userForm, bindingResult);
if (bindingResult.hasErrors()) {
return "registration";
}
usersService.save(userForm);
securityService.autoLogin(userForm.getUsername(), userForm.getpasswordconfirm());
return "redirect:/welcome";
}
@GetMapping("/login")
public String login(Model model, String error, String logout) {
if (error != null)
model.addAttribute("error", "Имя пользователя или пароль не верн.");
if (logout != null)
model.addAttribute("message", "Вы успешно вышли из системыy.");
return "login";
}
@GetMapping({"/", "/welcome"})
public String welcome(Model model) {
return "welcome";
}
}
For authenticating users I am using MySQL tables users, roles and users_roles. Sory for alot of code in question, but I am scroed up and have no idea where to look.
Login.JSP if it will be helpful:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}"/>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Вход</title>
<link href="../../webjars/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="../../webjars/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script src="../../webjars/jquery/3.0.0/js/jquery.min.js"></script>
</head>
<body>
<div class="container">
<form method="POST" action="${contextPath}/login" class="form-signin">
<h2 class="form-heading">Вход</h2>
<div class="form-group ${error != null ? 'has-error' : ''}">
<span>${message}</span>
<input name="username" type="text" class="form-control" placeholder="Логин"
autofocus="true"/>
<input name="password" type="password" class="form-control" placeholder="Пароль"/>
<span>${error}</span>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<button class="btn btn-lg btn-primary btn-block" type="submit">Вход</button>
<h4 class="text-center"><a href="${contextPath}/registration">Зарегистрироватся</a></h4>
</div>
</form>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
</body>
</html>
CodePudding user response:
if you look at hasRole
method you see it will add a ROLE_
prefix to it:
private static String hasRole(String role) {
Assert.notNull(role, "role cannot be null");
Assert.isTrue(!role.startsWith("ROLE_"), () -> {
return "role should not start with 'ROLE_' since it is automatically inserted. Got '" role "'";
});
return "hasRole('ROLE_" role "')";
}
I suspect that you named your role like ADMIN
or MANAGER
and use them like that. I suggest try to rename your roles and add ROLE_
prefix to them like ROLE_ADMIN
or ROLE_MANAGER
and try again.
here make sure when you getName()
your role name has a ROLE_
prefix like ROLE_ADMIN
for (Roles roles : user.getRoles()){
grantedAuthorities.add(new SimpleGrantedAuthority(roles.getName()));
}