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
What we can learn from cats

That's a fascinating observation, and you've touched upon something quite profound about the apparent inner peace that some animals seem...

 
 
 

Comments


Post: Blog2_Post

Subscribe Form

Thanks for submitting!

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

bottom of page