I have been trying to implement the administrator service spring security provides but I am finding it difficult, all my standard users can use the webapp but I cant seem to work out the sql required to authenticate that a user has a "ADMIN" role to allow them to view certain pages.
Any help advice would be greatly appreciated as I am new to this.
(Note: I am getting no errors in my console only the 403 username/password denied page)
My WebSecurityConfig file:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserLoginRepository userLoginRepository;
//http.authorizeRequests().antMatchers("/", "/home", "/registeruser").permitAll().antMatchers("/admin").hasRole("ADMIN")
#Autowired
DataSource dataSource;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/", "/home", "/registeruser").permitAll().antMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated().and().formLogin().loginPage("/login").permitAll().and().logout()
.permitAll();
http.exceptionHandling().accessDeniedPage("/403");
http.csrf().disable();
//disable csrf to allow communication (we also dont need for this fyp as its not live)
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//authorities at the moment is the manager i.e. 'Manager' from user_login
// String userByMailQuery = "SELECT user_type FROM user_login WHERE user_type = ?;";
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select user_name,password,user_status from user_login where user_name=?")
.authoritiesByUsernameQuery("select user_login_id, roles_id, role from user_login_roles, role where user_login_id=?");
}
}
The .authoritiesByUsernameQuery is the query im struggling with. The logic would be to check that a user has the role with the value of "ADMIN".
My UserLogin class:
#Entity
public class UserLogin {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
private Long phone;
private String userName;
private String address;
private String password;
private boolean userStatus;
private String userType;
private String position;
#OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
public Set<Role> roles;
#OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
public Set<PlayerSeasonStat> playerStats;
public UserLogin()
{
}
public UserLogin(Long id, String firstName, String lastName, Long phone,
String userName, String address, String password,
boolean userStatus, String userType, String position,
Set<Role> roles, Set<PlayerSeasonStat> playerStats) {
super();
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.phone = phone;
this.userName = userName;
this.address = address;
this.password = password;
this.userStatus = userStatus;
this.userType = userType;
this.position = position;
this.roles = roles;
this.playerStats = playerStats;
}
public Set<Role> getRoles() {
if (roles==null)
roles = new HashSet<>();
return roles;
}
public void setRole(Set<Role> roles) {
this.roles = roles;
}
public void addRole(Role role){
getRoles().add(role);
}
My role class:
#Entity
public class Role {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String role;
public Role()
{
}
public Role(Long id, String role) {
super();
this.id = id;
this.role = role;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
You can make a use of HandlerInterceptor where you can check login role is admin or not. with the method prehandle() of HandlerInterceptor interface you can handle the request and check the role and if login role does not match you can dispatch the request to 403 page
After login add Role Object to session, request.getSession().setAttribute("LOGGEDIN_USER_ROLE",roleObject) and you can make use of HandlerInterceptorAdapter
public class SecurityInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) throws Exception {
System.out.println("Interceptor: Pre-handle");
// Avoid a redirect loop for some urls
if(request.getRequestURI().equals("/admin-page")){
Role role = (Role) request.getSession().getAttribute("LOGGEDIN_USER_ROLE");
if(!role.getRole().equalsIgnoreCase("ADMIN")){
response.sendRedirect("/403/");
return false;
}
}
return true;
}
#Override
public void postHandle(HttpServletRequest request,HttpServletResponse response, Object handler) throws Exception {
}
}
Related
I am trying to list users in my application based on role. I have an endpoint in my controller which tries to call my userService class .findByRoles method but I dont know how to put the role I want to without having the role as a string(wont work).
adminController.java method:
#GetMapping(path="/users")
public String showUsers( Model model){
var userList = (List<User>) userService.findByRoles("ROLE_TEACHER"); // dont know how to pass a role here
model.addAttribute("userList", userList);
return "users";
}
User.java:
#Entity
#Table(name = "user_table", schema = "public")
public class User {
#Id
#Column(name = "user_id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long user_id;
#Column(name = "name")
private String name;
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
#Column(name = "confirmed")
private boolean confirmed;
#ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.DETACH}, fetch = FetchType.EAGER)
#JoinTable(
name = "user_role_table",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id", referencedColumnName = "role_id"))
private Collection<Role> roles = new HashSet<>();
/*******************************************
* Setters and getters
*******************************************/
public Long getId() {
return user_id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setUsername(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword() {
return password;
}
public void setEmail(String email) {
this.email = email;
}
public String getEmail() {
return email;
}
public void setConfirmed(Boolean confirmed) {
this.confirmed = confirmed;
}
public Boolean getConfirmed() {
return confirmed;
}
public void setRoles(Collection roles) {
this.roles = roles;
}
public Collection<Role> getRoles() { return this.roles;
}
}
userRepository:
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
User findByEmail(String email);
User findByRoles(Role role);
}
roleRepository:
public interface RoleRepository extends JpaRepository<Role, Long> {
Role findByName(String name);
}
IUserService.java:
public interface IUserService {
User registerNewUserAccount(UserDto userDto);
List<User> findAll();
Optional<User> findById(Long id);
User findByRoles(Role role);
public void delete(Long id);
public User updateConfirmed(Long id);
}
UserService.java method:
#Override
public User findByRoles(Role role) {
return userRepository.findByRoles(role);
}
Any tips how I can go about to fix this issue?
After tweaking around with the classes I think the most logical method to fix the issue is to use roleRepository method findByName. Furthermore, created findAllByRoles in userRepository, IUserService & UserService for returning a list(check my comment on this answer).
Changed IUserService.java to:
public interface IUserService {
User registerNewUserAccount(UserDto userDto);
List<User> findAll();
Optional<User> findById(Long id);
List<User> findAllByRoles(String roles); //Added this new method (VERY IMPORTANT for returning multiple users)
User findByRoles(String roles);
public void delete(Long id);
public User updateConfirmed(Long id);
}
Changed UserService.java method to:
#Override
public User findByRoles(String roles) {
return userRepository.findByRoles(roleRepository.findByName(roles)); // Using roleRepository here
}
// Added below findAllbyRoles to return a list
#Override
public List<User> findAllByRoles(String roles){
return userRepository.findAllByRoles(roleRepository.findByName(roles));
}
Changed adminController.java method to:
#GetMapping(path="/users")
public String showUsers( Model model){
List<User> userList = userService.findAllByRoles("ROLE_STUDENT");
userList.addAll(userService.findAllByRoles("ROLE_TEACHER"));
model.addAttribute("userList", userList);
return "users";
}
I am having a problem with my retriveUser method. When I run finbyid method it returns the same repeated values. You can see my repository, user, and userService classes and the result below.
[![findbyid method result][1]][1]
My User Repository Class
package io.javabrains.springsecurity.jpa;
import io.javabrains.*;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import io.javabrains.springsecurity.jpa.models.User;
#Repository
public interface UserRepository extends JpaRepository<User, Integer> {
Optional<User> findByUserName(String userName);
}
My User Class
#Entity
#Table(name="app_user")
public class User implements Serializable {
#Id
#GeneratedValue(strategy =GenerationType.IDENTITY)
private int id;
private String userName;
private String password;
private boolean active;
private String role;
private String city;
public User(String userName, boolean active, String role, String city) {
super();
this.userName = userName;
this.active = active;
this.role = role;
this.city = city;
}
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinTable(name = "user_cities", joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "city_id", referencedColumnName = "id"))
private Collection<UserCity> usercities = new ArrayList<UserCity>() ;
public Collection<UserCity> getUsercities() {
return usercities;
}
public void setUsercities(Collection<UserCity> usercities) {
this.usercities = usercities;
}
public User() {}
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 boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
My Service Class
#RestController
public class HomeResource {
#Autowired
private BCryptPasswordEncoder bcryptPasswordEncoder;
#Autowired
private WeatherService weatherService;
#Autowired
private CityRepository cityRepository;
#Autowired
private UserRepository userRepo;
#GetMapping("/")
public String home() {
return ("<h1>Welcome</h1>");
}
#GetMapping("/user")
public String user() {
return ("Welcome User");
}
#GetMapping("/admin")
public String admin() {
return ("<h1>Welcome Admin</h1>");
}
#GetMapping("/getCities")
public List<UserCity> getCities()
{
return cityRepository.findAll();
}
#GetMapping("/users/{id}")
public ResponseEntity<User> retriveUser(#PathVariable int id){
Optional<User> a=userRepo.findById(id);
return new ResponseEntity<User>(a.get(),HttpStatus.OK);
}
Thanks in advance for your help.
Sincerely
[1]: https://i.stack.imgur.com/gNhO7.png
The repeated value is a nested user from the usercities collection in a User object.
The user (id: 1) has a usercities collection containing one UserCity object (cityName: 'bursa' and users containing the same user (id: 1)). Thus, the user (id: 1) is recursively displayed.
You can add #JsonIgnore annotation to your property (usercities in User or users in UserCity) to cut the recursion.
I want to create a user within the run () method using CommandLineRunner. I want to automatically call this method and register it with JPA as soon as Tomcat runs. Below I share the codes I use within the project.When I ran it this way, the tomcat server worked fine, the system started but was not registered to the database. How do I fix this?
User
#Data
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private int id;
#Column(name = "email")
#Email(message = "*Please provide a valid Email")
#NotEmpty(message = "*Please provide an email")
private String email;
#Column(name = "password")
#Length(min = 5, message = "*Your password must have at least 5 characters")
#NotEmpty(message = "*Please provide your password")
private String password;
#Column(name = "name")
#NotEmpty(message = "*Please provide your name")
private String name;
#Column(name = "last_name")
#NotEmpty(message = "*Please provide your last name")
private String lastName;
#Column(name = "active")
private int active;
//JOIN tables
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"), inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles;
public User() {};
public User(String email, String password, String name, String lastName, int active) {
this.email=email;
this.password=password;
this.name=name;
this.lastName=lastName;
this.active=active;
}
//G&S
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public int getActive() {
return active;
}
public void setActive(int active) {
this.active = active;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
Role
#Data
#Entity
#Table(name = "role")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "role_id")
private int id;
#Column(name = "role")
private String role;
//G&S
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
UserRepository
#Repository("userRepository")
public interface UserRepository extends JpaRepository<User, Long> {
User findByEmail(String email);
}
RoleRepository
#Repository("roleRepository")
public interface RoleRepository extends JpaRepository<Role, Integer> {
Role findByRole(String role);
}
Service
public class ServiceImplement implements CommandLineRunner{
#Autowired
UserService userService;
#Autowired
UserRepository userRepository;
#Autowired
RoleRepository roleRepository;
//$2y$12$YN6IjSzj5DYvSpv6jofxV.p8C.zSKKTdnDZ4oc0XT0TmODcUe.s62 = umut1234
User umut = null;
Role userRole = null;
#Override
public void run(String... args) throws Exception {
umut = new User();
umut.setEmail("umut#umut.com");
umut.setPassword("$2y$12$YN6IjSzj5DYvSpv6jofxV.p8C.zSKKTdnDZ4oc0XT0TmODcUe.s62");
umut.setName("Umut");
umut.setLastName("Eren");
umut.setActive(1);
userRole = roleRepository.findByRole("ADMIN");
umut.setRoles(new HashSet<Role>(Arrays.asList(userRole)));
userRepository.save(umut);
System.out.println("***Generated***");
}
}
import org.springframework.boot.ApplicationArguments
import org.springframework.boot.ApplicationRunner
// add these 2 lines and change your impelentation to ApplicationRunner
#Component
#Order(value = 1)
public class ServiceImplement implements ApplicationRunner{
#Autowired
UserService userService;
#Autowired
UserRepository userRepository;
#Autowired
RoleRepository roleRepository;
//$2y$12$YN6IjSzj5DYvSpv6jofxV.p8C.zSKKTdnDZ4oc0XT0TmODcUe.s62 = umut1234
User umut = null;
Role userRole = null;
#Override
public void run(ApplicationArguments args) throws Exception {
umut = new User();
umut.setEmail("umut#umut.com");
umut.setPassword("$2y$12$YN6IjSzj5DYvSpv6jofxV.p8C.zSKKTdnDZ4oc0XT0TmODcUe.s62");
umut.setName("Umut");
umut.setLastName("Eren");
umut.setActive(1);
//save user before add roles to it
userRepository.save(umut);
userRole = roleRepository.findByRole("ADMIN");
umut.setRoles(new HashSet<Role>(Arrays.asList(userRole)));
userRepository.save(umut);
System.out.println("***Generated***");
}
}
public class ServiceImplement implements ApplicationRunner {
#Autowired
UserService userService;
#Autowired
UserRepository userRepository;
#Autowired
RoleRepository roleRepository;
User umut = null;
Role userRole = null;
#Override
public void run(ApplicationArguments args) throws Exception {
String arguments = String.join(" | ", args.getSourceArgs());
System.out.println(arguments);
if(args.getNonOptionArgs().contains("-register"){
umut = new User();
umut.setEmail("umut#umut.com");
umut.setPassword("$2y$12$YN6IjSzj5DYvSpv6jofxV.p8C.zSKKTdnDZ4oc0XT0TmODcUe.s62");
umut.setName("Umut");
umut.setLastName("Eren");
umut.setActive(1);
userRole = roleRepository.findByRole("ADMIN");
umut.setRoles(new HashSet<Role>(Arrays.asList(userRole)));
userRepository.save(umut);
System.out.println("***Generated***");
}
}
}
Hi everyone i was started to make from scratch my Spring Boot Application. I want to make Booking Application for my portfolio. I am a begginner in coding,past one year I have learning Java. I'm struggling whole day and I don't know where I have problem when I want to call getUser method in my Controller Class? Simply controller class doesn't see
method's is my Service Class.
Class's are in four different packages
com.booking.controller
com.booking.model
com.booking.repository
com.service
Here I have problem, simply it can't see service class.
#Controller
#RequestMapping(path = "/user")
public class UserController{
#Autowired
private UserRepository userRepository;
#GetMapping(path = "/add")
public #ResponseBody String addNewUser(#RequestParam String name,
#RequestParam String username,
#RequestParam String email, #RequestParam String password) {
User n = new User();
n.setName(name);
n.setUsername(username);
n.setEmail(email);
n.setPassword(password);
userRepository.save(n);
return "Saved";
}
#GetMapping(path = "/all")
public #ResponseBody Iterable<User> getAllUsers() {
return userRepository.findAll();
}
#RequestMapping("/{Id}")
public User getUser(#PathVariable Long id) {
return userRepository.getUser(id);
/*Here you can see problem*/
}
}
My User Class
#Entity
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column
private String name;
#Column
private String username;
#Column
private String email;
#Column
private String password;
public User() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
**My Repository Class--
public interface UserRepository extends CrudRepository<User, Long> {
public List<User> findByUsername(String username);
}
My Service Class
#Service
public class UserService {
#Autowired
private UserRepository userRepository;
public List<User> getAllUsers(String username) {
List<User> users = new ArrayList<>();
userRepository.findByUsername(username).forEach(users::add);
return users;
}
public User getUser(Long id) {
return userRepository.findOne(id);
}
public void addUser(User user) {
userRepository.save(user);
}
public void updateUser(Long id, User user) {
userRepository.save(user);
}
public void deleteUser(Long id) {
userRepository.delete(id);
}
}
I have a toy web app, in which I want to login the users taken from database.
It works but, I can login with a USER role where I should only login with a user having the ADMIN role.
Here is my code:
Servlet 3.0 bootstraper
public class Bootstraper extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{JPAConfig.class, WebSecurityConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{MvcConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
#Override
protected Filter[] getServletFilters() {
return new Filter[]{new DelegatingFilterProxy("springSecurityFilterChain"), new OpenEntityManagerInViewFilter()};
}
}
User entity:
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(unique = true)
private String name;
#Column
private String password;
#Column
private boolean enabled;
#OneToMany(mappedBy = "user", fetch = FetchType.EAGER)
private Set<Role> roles = new HashSet<>();
public User() {
}
public User(String name, String password, boolean enabled) {
this.name = name;
this.password = password;
this.enabled = enabled;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
#Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
", enabled=" + enabled +
'}';
}
}
Role entity:
#Entity
#Table(name = "roles")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column
private String role;
#JoinColumn(name = "u_id")
#ManyToOne
private User user;
public Role() {
}
public Role(String role) {
this.role = role;
}
public Long getId() {
return id;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
#Override
public String toString() {
return "Role{" +
"id=" + id +
", role='" + role + '\'' +
", user=" + user +
'}';
}
}
My custome userdetailsservice:
#Service
public class MyUserDetailsService implements UserDetailsService {
#Autowired
private UsersService usersService;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = usersService.getUser(username);
if (user != null) {
List<GrantedAuthority> authorityList = user.getRoles().stream()
.map(role -> new SimpleGrantedAuthority(role.getRole()))
.collect(Collectors.toList());
return new org.springframework.security.core.userdetails.User(username, user.getPassword(), user.isEnabled(), true, true, true, authorityList);
}
return null;
}
}
I am keeping my roles in the db, as strings : USER, MODERATOR, ADMIN
My Spring security config:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(WebSecurityConfig.class);
#Autowired
private MyUserDetailsService userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.anyRequest().authenticated()
.antMatchers("/**").access("hasRole('ROLE_ADMIN')")
.and().formLogin()
.loginPage("/login")
.permitAll().and()
.logout().permitAll();
http.csrf().disable();
}
}
The problem is guys, that I said I want every user to have the ROLE_ADMIN but I am still able to login also with a user that has only the USER role. I don't understand why.
I have made debug, my user is fetched successfully from the db, all is fine,but I don't know where Spring is checking for roles.
Regards,
remove your .anyRequest().authenticated() from configuration since if you request ROLE checking user has to be authenticated it apparently takes precedence when checked by the security filter
as user3030447 said in a comment:
Roles must start with ROLE_*.
e.g. if you want to use hasRole('EDIT') you will have to create the GrantedAuthority like this:
new SimpleGrantedAuthority("ROLE_EDIT");
You can use hasAuthority() or hasAnyAuthority() functions.