top of page
Search

Spring Security

Let's turn this into a more structured tutorial on Spring Security and roles.


## Spring Security and Roles: A Practical Tutorial


This tutorial demonstrates how to implement role-based authorization in a Spring Boot REST API using Spring Security.  We'll cover user authentication, role assignment, and how to protect API endpoints based on roles.


1. Project Setup (using Spring Initializr):


Include the following dependencies:


* Spring Web

* Spring Security

* Spring Data JPA (or your preferred data access method)

* A database driver (e.g., H2, MySQL, PostgreSQL)

* Lombok (optional, for reducing boilerplate)

* JWT library (e.g., jjwt)


2. User Entity (AppUser):


```java

@Entity

@Data

public class AppUser {


    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Long id;


    @Column(unique = true, nullable = false)

    private String username;


    @Column(nullable = false)

    private String password; // Store securely hashed!


    @ElementCollection(fetch = FetchType.EAGER) // Or a separate Role entity for more complex scenarios

    @CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id")) // Specify table for roles

    @Column(name = "role") // Specify column for role

    private List<String> roles; // List of roles (e.g., "ROLE_ADMIN", "ROLE_CLIENT")


    // ... other fields

}

```


3. User Repository:


```java

public interface UserRepository extends JpaRepository<AppUser, Long> {

    AppUser findByUsername(String username);

}

```


4. User Service (Implementing UserDetailsService):


```java

@Service

@RequiredArgsConstructor

public class UserService implements UserDetailsService {


    private final UserRepository userRepository;

    private final PasswordEncoder passwordEncoder;


    public AppUser signup(AppUser user) {

        user.setPassword(passwordEncoder.encode(user.getPassword()));

        return userRepository.save(user);

    }


    @Override

    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        AppUser appUser = userRepository.findByUsername(username);

        if (appUser == null) {

            throw new UsernameNotFoundException("User not found");

        }


        // Convert roles to GrantedAuthorities

        List<GrantedAuthority> authorities = appUser.getRoles().stream()

                .map(role -> new SimpleGrantedAuthority(role))

                .collect(Collectors.toList());


        return new User(appUser.getUsername(), appUser.getPassword(), authorities);

    }


    // ... other methods (e.g., retrieving user by username)

}

```


5. Security Configuration:


```java

@Configuration

@EnableWebSecurity

@EnableMethodSecurity // Enable method-level security annotations like @PreAuthorize

@RequiredArgsConstructor

public class SecurityConfig {


    private final UserDetailsService userDetailsService;

    private final JwtAuthenticationFilter jwtAuthenticationFilter; // Your JWT filter


    @Bean

    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        http

            .csrf().disable()

            .cors().and()

            .authorizeHttpRequests(authorize -> authorize

                .antMatchers("/auth/**").permitAll() // Allow authentication endpoints

                .antMatchers("/admin/**").hasRole("ADMIN") // Protect admin endpoints

                .antMatchers("/client/**").hasAnyRole("ADMIN", "CLIENT") // Protect client endpoints

                .anyRequest().authenticated() // All other endpoints require authentication

            )

            .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)

            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));


        return http.build();

    }


    @Bean

    public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {

        return authConfig.getAuthenticationManager();

    }



    @Bean

    public PasswordEncoder passwordEncoder() {

        return new BCryptPasswordEncoder(); // Use a strong password encoder

    }

}

```


6. JWT Filter (JwtAuthenticationFilter - Example):


```java

public class JwtAuthenticationFilter extends OncePerRequestFilter {


    // ... (Inject JWT utility class, UserDetailsService, etc.)


    @Override

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)

            throws ServletException, IOException {


        String token = extractJwtToken(request); // Helper function to extract token


        if (token != null && jwtUtil.validateToken(token)) {

            String username = jwtUtil.getUsernameFromToken(token);


            UserDetails userDetails = userDetailsService.loadUserByUsername(username);

            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(

                    userDetails, null, userDetails.getAuthorities()); // Get authorities (roles)


            SecurityContextHolder.getContext().setAuthentication(authentication); // Set authentication in context

        }


        filterChain.doFilter(request, response);

    }


    // ... (Helper functions: extractJwtToken, etc.)

}

```


7. Controller Example:


```java

@RestController

@RequestMapping("/admin")

@PreAuthorize("hasRole('ROLE_ADMIN')") // Secure at the controller level

public class AdminController {


    @GetMapping("/dashboard")

    public String adminDashboard() {

        return "Admin Dashboard (Accessible only by admins)";

    }

}


@RestController

@RequestMapping("/client")

public class ClientController {


    @GetMapping("/profile")

    @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_CLIENT')")  // Both admin and client can access

    public String clientProfile() {

        return "Client Profile";

    }


    @GetMapping("/data")

    @PreAuthorize("hasRole('ROLE_CLIENT')") // Only client can access

    public String clientData() {

        return "Client Data";

    }

}


@RestController

@RequestMapping("/auth")

public class AuthenticationController {


    // ... signup and signin endpoints

}

```


8. Key Improvements and Explanations:


*`@EnableMethodSecurity`:** This annotation is crucial for using `@PreAuthorize` in your controllers.

*`UserDetailsService`:** The `loadUserByUsername` method is where you retrieve user details (including roles) from your database and convert them into `UserDetails` objects.  This is the bridge between your user data and Spring Security.

*`GrantedAuthority`:**  Roles are represented as `GrantedAuthority` objects in Spring Security.

*`@PreAuthorize`:** This annotation allows you to secure individual methods based on roles.  `hasRole()` checks for a specific role (e.g., "ROLE_ADMIN"), while `hasAnyRole()` checks for any of the specified roles.

*Role Prefix:** Spring Security automatically adds a "ROLE_" prefix to roles (e.g., "ADMIN" becomes "ROLE_ADMIN").  This is why you use "ROLE_ADMIN" in `@PreAuthorize`.

*JWT Filter:** The `JwtAuthenticationFilter` (or your authentication mechanism) is responsible for extracting the JWT (or other credentials), validating it, and setting the authentication in the `SecurityContextHolder`.  This is where the roles from the JWT are loaded into the `Authentication` object.

*Database Design:**  Consider a separate `Role` entity for more complex role management.  This is especially useful if you have hierarchical roles or permissions.

*Password Hashing:**  Use `PasswordEncoder` (like `BCryptPasswordEncoder`) to hash passwords before storing them in the database.  Never store passwords in plain text.


9. Testing:


Use tools like Postman or curl to test your API endpoints.  Make sure to include the JWT in the `Authorization` header for protected endpoints.  Test with different users having different roles to verify that the authorization is working correctly.


This tutorial provides a comprehensive example of how to implement role-based authorization using Spring Security.  Remember to adapt the code to your specific needs and always prioritize security best practices.

 
 
 

Recent Posts

See All

Comments


Post: Blog2_Post

Subscribe Form

Thanks for submitting!

©2020 by LearnTeachMaster DevOps. Proudly created with Wix.com

bottom of page