Home >Java >javaTutorial >Implementing Token-Based Authentication in Spring Boot Using Spring Security, JWT, and JDBC Template
Github Link for the Code Reference
In modern web applications, secure user authentication is crucial. Traditionally, session-based authentication has been widely used, but as applications become more distributed and scalable, token-based authentication offers several advantages.
Token-based authentication allows applications to be stateless, meaning that the server doesn’t need to store any session data, making it well-suited for scalable, RESTful APIs.
This tutorial will guide you through implementing JWT (JSON Web Token) authentication in a Spring Boot application using Spring Security with JDBC Template.
JWT(JSON Web Token) is a compact, URL-safe way of representing claims transferred between two parties. It's commonly used for stateless authentication where each request is authenticated using a signed token.
Stateless Authentication
JWT tokens are self-contained, carrying the user’s authentication information directly within the token payload, which reduces server memory usage and increases scalability.
Cross-Platform Support
Tokens are easy to use in mobile and web applications since they can be stored securely in the client (e.g., in local storage or cookies).
Security
Each token is digitally signed, ensuring its integrity and allowing the server to verify it without querying a database on every request.
In this tutorial, you will learn how to:
By the end of this tutorial, you’ll have a secure, stateless authentication system that leverages Spring Boot and JWT to provide seamless and scalable access control for your applications.
API Flow:
Technology requirements:
Use Spring web tool or your development tool (STS, Intellij or any IDE) to create a Spring Boot project.
open pom.xml and add dependencies for Spring Security, JWT, and JDBC Template:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> </dependency>
Under src/main/resources folder, open application.properties and add below configurations. I will be using postgres database for this tutorial.
spring.application.name= authmanager server.port= 1001 servlet.context-path= /authmanager database.username= postgres database.password= admin@123 database.driverClassName= org.postgresql.Driver database.jdbcUrl= jdbc:postgresql://localhost:5432/postgres database.maxActive= 5 database.minIdle= 5 database.poolName= Authmanager Postgres Datasource app.jwtSecret= ###############ib-Spring############### app.jwtExpirationMs= 3600000 app.jwtRefreshExpirationMs= 86400000
Define a simple table structure for user information, roles, user-role mapping and refresh tokens:
CREATE SCHEMA IB; ------------------------------------------------------------------------- create sequence users_uniqueid_seq START 1; create table ib.users( uniqueid bigint not null default nextval('users_uniqueid_seq') PRIMARY KEY, email varchar(75), password varchar(200), username varchar(20) ); insert into ib.users(email,password,username) values ('admin@ib.com','a$VcdzH8Q.o4KEo6df.XesdOmXdXQwT5ugNQvu1Pl0390rmfOeA1bhS','admin'); #(password = 12345678) ------------------------------------------------------------------------- create sequence roles_id_seq START 1; create table ib.roles( id int not null default nextval('roles_id_seq') PRIMARY KEY, name varchar(20) ); INSERT INTO ib.roles(name) VALUES('ROLE_USER'); INSERT INTO ib.roles(name) VALUES('ROLE_MODERATOR'); INSERT INTO ib.roles(name) VALUES('ROLE_ADMIN'); ------------------------------------------------------------------------- create table ib.user_roles( user_uniqueid bigint not null, role_id int not null, primary key(user_uniqueid,role_id) ); insert into ib.user_roles (user_uniqueid,role_id) values (1,3); ------------------------------------------------------------------------- create sequence refresh_tokens_id_seq START 1; create table ib.refresh_tokens( id bigint not null default nextval('refresh_tokens_id_seq') PRIMARY KEY, uniqueid bigint, token varchar(500) not null, expiryDate TIMESTAMP WITH TIME ZONE not null ); -------------------------------------------------------------------------
Let’s define the following models.
In models package, create below 4 files:
model/ERole.java
package com.security.authmanager.model; public enum ERole { ROLE_USER, ROLE_MODERATOR, ROLE_ADMIN }
model/Role.java
package com.security.authmanager.model; public class Role { private Integer id; private ERole name; public Role() { } public Role(ERole name) { this.name = name; } //generate getters and setters }
model/User.java
package com.security.authmanager.model; import java.util.HashSet; import java.util.Set; public class User { private Long id; private String username; private String email; private String password; private Set<Role> roles = new HashSet<>(); public User() { } public User(String username, String email, String password) { this.username = username; this.email = email; this.password = password; } //generate getters and setters }
model/RefreshToken.java
package com.security.authmanager.model; import java.util.HashSet; import java.util.Set; public class User { private Long id; private String username; private String email; private String password; private Set<Role> roles = new HashSet<>(); public User() { } public User(String username, String email, String password) { this.username = username; this.email = email; this.password = password; } //generate getters and setters }
The CustomUserDetailsRepository class is a Spring @Repository that handles custom database operations related to User and Role entities. It uses JdbcTemplate to execute SQL queries for tasks like fetching users, checking if a user exists by username or email, creating new users, and fetching roles.
package com.security.authmanager.repository; import com.security.authmanager.common.QueryConstants; import com.security.authmanager.model.ERole; import com.security.authmanager.model.Role; import com.security.authmanager.model.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; import java.util.HashSet; import java.util.Objects; import java.util.Set; @Repository public class CustomUserDetailsRepository { private static final Logger logger = LoggerFactory.getLogger(CustomUserDetailsRepository.class); @Autowired private JdbcTemplate jdbcTemplate; public User fetchUserByUserName(String userName){ try{ return jdbcTemplate.query((conn) ->{ final PreparedStatement ps = conn.prepareStatement(QueryConstants.FETCH_USER); ps.setString(1, userName.toUpperCase()); return ps; },rs->{ User user = null; Set<Role> roles = new HashSet<>(); while (rs.next()) { if (user == null) { user = new User(); user.setEmail(rs.getString("email")); user.setId(rs.getLong("uniqueid")); user.setPassword(rs.getString("password")); user.setUsername(rs.getString("username")); } Role role = new Role(); role.setId(rs.getInt("id")); role.setName(ERole.valueOf(rs.getString("name"))); roles.add(role); } if (user != null) { user.setRoles(roles); } return user; }); }catch(Exception e){ logger.error("Exception in fetchUserByUserName()",e); throw new RuntimeException(e); } } public boolean existsByUsername(String userName) { try{ return jdbcTemplate.query((conn) -> { final PreparedStatement ps = conn.prepareStatement(QueryConstants.CHECK_USER_BY_USERNAME); ps.setString(1, userName.toUpperCase()); return ps; }, (rs,rownum) -> rs.getInt("count")).get(0)>0; }catch(Exception e){ logger.error("Exception in existsByUsername()",e); throw new RuntimeException(e); } } public boolean existsByEmail(String email) { try{ return jdbcTemplate.query((conn) -> { final PreparedStatement ps = conn.prepareStatement(QueryConstants.CHECK_USER_BY_EMAIL); ps.setString(1, email.toUpperCase()); return ps; }, (rs,rownum) -> rs.getInt("count")).get(0)>0; }catch(Exception e){ logger.error("Exception in existsByEmail()",e); throw new RuntimeException(e); } } public Role findRoleByName(ERole eRole) { try{ return jdbcTemplate.query((conn) -> { final PreparedStatement ps = conn.prepareStatement(QueryConstants.FETCH_ROLE_BY_NAME); ps.setString(1, String.valueOf(eRole)); return ps; }, rs -> { Role role=null; while(rs.next()){ role = new Role(); role.setName(ERole.valueOf(rs.getString("name"))); role.setId(rs.getInt("id")); } return role; }); }catch(Exception e){ logger.error("Exception in findRoleByName()",e); throw new RuntimeException(e); } } public void createUser(User user) { try(Connection conn = Objects.requireNonNull(jdbcTemplate.getDataSource()).getConnection()){ try (PreparedStatement userStatement = conn.prepareStatement(QueryConstants.INSERT_TO_USERS,Statement.RETURN_GENERATED_KEYS)) { userStatement.setString(1, user.getEmail().toUpperCase()); userStatement.setString(2, user.getPassword()); userStatement.setString(3, user.getUsername().toUpperCase()); userStatement.executeUpdate(); // Retrieve generated userId try (ResultSet generatedKeys = userStatement.getGeneratedKeys()) { if (generatedKeys.next()) { Long userId = generatedKeys.getLong(1); // Assuming userId is of type VARCHAR logger.info("gen userid {}",userId.toString()); user.setId(userId); } } } if (user.getRoles() != null && !user.getRoles().isEmpty()) { try (PreparedStatement userRoleStatement = conn.prepareStatement(QueryConstants.INSERT_TO_USER_ROLES)) { for (Role role : user.getRoles()) { userRoleStatement.setLong(1, user.getId()); userRoleStatement.setLong(2, role.getId()); userRoleStatement.executeUpdate(); } } } }catch(Exception e){ logger.error("Exception in existsByEmail()",e); throw new RuntimeException(e); } } }
This repository performs custom SQL-based CRUD operations for managing User and Role data in the database.
Key Functions:
The RefreshTokenRepository class is a Spring @Repository that handles database operations related to the RefreshToken entity. It uses Spring's JdbcTemplate for interacting with the database through raw SQL queries, encapsulating the logic for saving, deleting, and retrieving refresh tokens.
package com.security.authmanager.repository; import com.security.authmanager.common.QueryConstants; import com.security.authmanager.model.ERole; import com.security.authmanager.model.RefreshToken; import com.security.authmanager.model.Role; import com.security.authmanager.model.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import java.sql.PreparedStatement; import java.sql.Timestamp; import java.util.Optional; @Repository public class RefreshTokenRepository { private static final Logger logger = LoggerFactory.getLogger(RefreshTokenRepository.class); @Autowired private JdbcTemplate jdbcTemplate; public void deleteRefreshToken(RefreshToken refreshToken) { try{ jdbcTemplate.update(QueryConstants.DELETE_REFRESH_TOKEN,(final PreparedStatement ps) ->{ ps.setString(1,refreshToken.getToken()); }); }catch (Exception e){ logger.error("Exception in deleteRefreshToken()",e); throw new RuntimeException(e); } } public int deleteRefreshTokenByUser(User user) { return 0; } public RefreshToken saveRefreshToken(RefreshToken refreshToken) { try{ jdbcTemplate.update(QueryConstants.SAVE_REFRESH_TOKEN,(final PreparedStatement ps) ->{ ps.setLong(1,refreshToken.getUser().getId()); ps.setString(2,refreshToken.getToken()); ps.setTimestamp(3, Timestamp.from(refreshToken.getExpiryDate())); }); }catch (Exception e){ logger.error("Exception in saveRefreshToken()",e); throw new RuntimeException(e); } return refreshToken; } public Optional<RefreshToken> findByToken(String token) { RefreshToken refreshToken = new RefreshToken(); try{ return Optional.ofNullable(jdbcTemplate.query((conn) -> { final PreparedStatement ps = conn.prepareStatement(QueryConstants.FIND_BY_TOKEN); ps.setString(1, token); return ps; }, rs -> { User user = new User(); while (rs.next()) { refreshToken.setId(rs.getLong("id")); refreshToken.setToken(rs.getString("token")); refreshToken.setExpiryDate(rs.getTimestamp("expiryDate").toInstant()); user.setId(rs.getLong("uniqueid")); user.setEmail(rs.getString("email")); user.setUsername(rs.getString("username")); refreshToken.setUser(user); } return refreshToken; })); }catch(Exception e){ logger.error("Exception in findByToken()",e); throw new RuntimeException(e); } } }
This repository interacts directly with the database for CRUD operations on RefreshToken entities.
Key Functions:
WebSecurityConfig configures Spring Security to use JWT-based token authentication.
It defines beans for various components needed for authentication like AuthTokenFilter (for handling JWT tokens), DaoAuthenticationProvider (for retrieving user details and validating passwords), and BCryptPasswordEncoder (for hashing and comparing passwords).
The SecurityFilterChain configures how incoming HTTP requests are secured:
- Permits access to certain public routes (/auth/**, /test/**).
- Protects all other routes, requiring authentication.
- Disables session management (making the system stateless).
- Configures a filter to intercept and process JWT tokens for
user authentication.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> </dependency>
This class is primarily used in Spring Security to represent the currently authenticated user.
When a user tries to log in:
UserDetailsImpl is a bridge between your User entity and Spring Security's internal mechanisms for authentication and authorization.
spring.application.name= authmanager server.port= 1001 servlet.context-path= /authmanager database.username= postgres database.password= admin@123 database.driverClassName= org.postgresql.Driver database.jdbcUrl= jdbc:postgresql://localhost:5432/postgres database.maxActive= 5 database.minIdle= 5 database.poolName= Authmanager Postgres Datasource app.jwtSecret= ###############ib-Spring############### app.jwtExpirationMs= 3600000 app.jwtRefreshExpirationMs= 86400000
Below are the queries : (QueryConstants.java)
CREATE SCHEMA IB; ------------------------------------------------------------------------- create sequence users_uniqueid_seq START 1; create table ib.users( uniqueid bigint not null default nextval('users_uniqueid_seq') PRIMARY KEY, email varchar(75), password varchar(200), username varchar(20) ); insert into ib.users(email,password,username) values ('admin@ib.com','a$VcdzH8Q.o4KEo6df.XesdOmXdXQwT5ugNQvu1Pl0390rmfOeA1bhS','admin'); #(password = 12345678) ------------------------------------------------------------------------- create sequence roles_id_seq START 1; create table ib.roles( id int not null default nextval('roles_id_seq') PRIMARY KEY, name varchar(20) ); INSERT INTO ib.roles(name) VALUES('ROLE_USER'); INSERT INTO ib.roles(name) VALUES('ROLE_MODERATOR'); INSERT INTO ib.roles(name) VALUES('ROLE_ADMIN'); ------------------------------------------------------------------------- create table ib.user_roles( user_uniqueid bigint not null, role_id int not null, primary key(user_uniqueid,role_id) ); insert into ib.user_roles (user_uniqueid,role_id) values (1,3); ------------------------------------------------------------------------- create sequence refresh_tokens_id_seq START 1; create table ib.refresh_tokens( id bigint not null default nextval('refresh_tokens_id_seq') PRIMARY KEY, uniqueid bigint, token varchar(500) not null, expiryDate TIMESTAMP WITH TIME ZONE not null ); -------------------------------------------------------------------------
The UserDetailsServiceImpl class acts as a bridge between your application's database and Spring Security’s authentication process. It fetches user details from the database using CustomUserDetailsRepository, converts the User object into UserDetailsImpl (a Spring Security-friendly format), and handles cases where a user is not found by throwing an exception. This service allows Spring Security to authenticate users and manage authorization based on the user's roles and permissions.
package com.security.authmanager.model; public enum ERole { ROLE_USER, ROLE_MODERATOR, ROLE_ADMIN }
The AuthTokenFilter class extends Spring's OncePerRequestFilter, making it a filter that processes every HTTP request once in a request chain. Its primary role is to extract and validate a JWT (JSON Web Token) from the request and set the user’s authentication in Spring Security's SecurityContext if the token is valid.
package com.security.authmanager.model; public class Role { private Integer id; private ERole name; public Role() { } public Role(ERole name) { this.name = name; } //generate getters and setters }
Each time a request is made:
This filter ensures that all requests carrying valid JWT tokens are authenticated automatically, without the need for the user to provide credentials (like username/password) in subsequent requests after logging in.
The RefreshTokenService class provides services related to managing refresh tokens in a token-based authentication system. Refresh tokens are used to obtain new JWT tokens after the initial JWT has expired without requiring the user to re-authenticate.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> </dependency>
RefreshTokenService handles the creation, validation, and deletion of refresh tokens. It uses repositories for saving and fetching tokens and users from the database.
This service is an essential part of an authentication system where refresh tokens are used to keep users logged in without requiring them to provide credentials again after the JWT expires.
The JwtUtils class is a utility class that handles the creation, parsing, and validation of JWT (JSON Web Token) for authentication purposes in a Spring Boot application. It uses the jjwt library to work with JWTs.
spring.application.name= authmanager server.port= 1001 servlet.context-path= /authmanager database.username= postgres database.password= admin@123 database.driverClassName= org.postgresql.Driver database.jdbcUrl= jdbc:postgresql://localhost:5432/postgres database.maxActive= 5 database.minIdle= 5 database.poolName= Authmanager Postgres Datasource app.jwtSecret= ###############ib-Spring############### app.jwtExpirationMs= 3600000 app.jwtRefreshExpirationMs= 86400000
The JwtUtils class is responsible for generating, parsing, and validating JWT tokens. It securely signs tokens using a secret key (HMAC-SHA256) and ensures that the tokens can only be read or verified by parties that possess the correct secret key.
The class also extracts the username from the token and checks if the token is valid before granting access to the user. This utility is essential for maintaining secure, token-based authentication in your application.
The AuthEntryPointJwt class implements Spring Security's AuthenticationEntryPoint interface. It handles what happens when an unauthorized request is made, typically when a user tries to access a protected resource without valid authentication (e.g., no JWT or an invalid JWT).
CREATE SCHEMA IB; ------------------------------------------------------------------------- create sequence users_uniqueid_seq START 1; create table ib.users( uniqueid bigint not null default nextval('users_uniqueid_seq') PRIMARY KEY, email varchar(75), password varchar(200), username varchar(20) ); insert into ib.users(email,password,username) values ('admin@ib.com','a$VcdzH8Q.o4KEo6df.XesdOmXdXQwT5ugNQvu1Pl0390rmfOeA1bhS','admin'); #(password = 12345678) ------------------------------------------------------------------------- create sequence roles_id_seq START 1; create table ib.roles( id int not null default nextval('roles_id_seq') PRIMARY KEY, name varchar(20) ); INSERT INTO ib.roles(name) VALUES('ROLE_USER'); INSERT INTO ib.roles(name) VALUES('ROLE_MODERATOR'); INSERT INTO ib.roles(name) VALUES('ROLE_ADMIN'); ------------------------------------------------------------------------- create table ib.user_roles( user_uniqueid bigint not null, role_id int not null, primary key(user_uniqueid,role_id) ); insert into ib.user_roles (user_uniqueid,role_id) values (1,3); ------------------------------------------------------------------------- create sequence refresh_tokens_id_seq START 1; create table ib.refresh_tokens( id bigint not null default nextval('refresh_tokens_id_seq') PRIMARY KEY, uniqueid bigint, token varchar(500) not null, expiryDate TIMESTAMP WITH TIME ZONE not null ); -------------------------------------------------------------------------
The AuthEntryPointJwt class is a custom entry point that intercepts unauthorized access attempts and returns a structured JSON response with a 401 error code, an error message, and details about the request.
It logs the error and provides a clear, user-friendly response to clients when authentication fails.
Below are the payloads for our RestAPIs:
1. Requests:
- LoginRequest.java :
package com.security.authmanager.model; public enum ERole { ROLE_USER, ROLE_MODERATOR, ROLE_ADMIN }
- SignupRequest.java :
package com.security.authmanager.model; public class Role { private Integer id; private ERole name; public Role() { } public Role(ERole name) { this.name = name; } //generate getters and setters }
- TokenRefreshRequest.java :
package com.security.authmanager.model; import java.util.HashSet; import java.util.Set; public class User { private Long id; private String username; private String email; private String password; private Set<Role> roles = new HashSet<>(); public User() { } public User(String username, String email, String password) { this.username = username; this.email = email; this.password = password; } //generate getters and setters }
2. Responses:
- JwtResponse.java
package com.security.authmanager.model; import java.util.HashSet; import java.util.Set; public class User { private Long id; private String username; private String email; private String password; private Set<Role> roles = new HashSet<>(); public User() { } public User(String username, String email, String password) { this.username = username; this.email = email; this.password = password; } //generate getters and setters }
- MessageResponse.java
package com.security.authmanager.repository; import com.security.authmanager.common.QueryConstants; import com.security.authmanager.model.ERole; import com.security.authmanager.model.Role; import com.security.authmanager.model.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; import java.util.HashSet; import java.util.Objects; import java.util.Set; @Repository public class CustomUserDetailsRepository { private static final Logger logger = LoggerFactory.getLogger(CustomUserDetailsRepository.class); @Autowired private JdbcTemplate jdbcTemplate; public User fetchUserByUserName(String userName){ try{ return jdbcTemplate.query((conn) ->{ final PreparedStatement ps = conn.prepareStatement(QueryConstants.FETCH_USER); ps.setString(1, userName.toUpperCase()); return ps; },rs->{ User user = null; Set<Role> roles = new HashSet<>(); while (rs.next()) { if (user == null) { user = new User(); user.setEmail(rs.getString("email")); user.setId(rs.getLong("uniqueid")); user.setPassword(rs.getString("password")); user.setUsername(rs.getString("username")); } Role role = new Role(); role.setId(rs.getInt("id")); role.setName(ERole.valueOf(rs.getString("name"))); roles.add(role); } if (user != null) { user.setRoles(roles); } return user; }); }catch(Exception e){ logger.error("Exception in fetchUserByUserName()",e); throw new RuntimeException(e); } } public boolean existsByUsername(String userName) { try{ return jdbcTemplate.query((conn) -> { final PreparedStatement ps = conn.prepareStatement(QueryConstants.CHECK_USER_BY_USERNAME); ps.setString(1, userName.toUpperCase()); return ps; }, (rs,rownum) -> rs.getInt("count")).get(0)>0; }catch(Exception e){ logger.error("Exception in existsByUsername()",e); throw new RuntimeException(e); } } public boolean existsByEmail(String email) { try{ return jdbcTemplate.query((conn) -> { final PreparedStatement ps = conn.prepareStatement(QueryConstants.CHECK_USER_BY_EMAIL); ps.setString(1, email.toUpperCase()); return ps; }, (rs,rownum) -> rs.getInt("count")).get(0)>0; }catch(Exception e){ logger.error("Exception in existsByEmail()",e); throw new RuntimeException(e); } } public Role findRoleByName(ERole eRole) { try{ return jdbcTemplate.query((conn) -> { final PreparedStatement ps = conn.prepareStatement(QueryConstants.FETCH_ROLE_BY_NAME); ps.setString(1, String.valueOf(eRole)); return ps; }, rs -> { Role role=null; while(rs.next()){ role = new Role(); role.setName(ERole.valueOf(rs.getString("name"))); role.setId(rs.getInt("id")); } return role; }); }catch(Exception e){ logger.error("Exception in findRoleByName()",e); throw new RuntimeException(e); } } public void createUser(User user) { try(Connection conn = Objects.requireNonNull(jdbcTemplate.getDataSource()).getConnection()){ try (PreparedStatement userStatement = conn.prepareStatement(QueryConstants.INSERT_TO_USERS,Statement.RETURN_GENERATED_KEYS)) { userStatement.setString(1, user.getEmail().toUpperCase()); userStatement.setString(2, user.getPassword()); userStatement.setString(3, user.getUsername().toUpperCase()); userStatement.executeUpdate(); // Retrieve generated userId try (ResultSet generatedKeys = userStatement.getGeneratedKeys()) { if (generatedKeys.next()) { Long userId = generatedKeys.getLong(1); // Assuming userId is of type VARCHAR logger.info("gen userid {}",userId.toString()); user.setId(userId); } } } if (user.getRoles() != null && !user.getRoles().isEmpty()) { try (PreparedStatement userRoleStatement = conn.prepareStatement(QueryConstants.INSERT_TO_USER_ROLES)) { for (Role role : user.getRoles()) { userRoleStatement.setLong(1, user.getId()); userRoleStatement.setLong(2, role.getId()); userRoleStatement.executeUpdate(); } } } }catch(Exception e){ logger.error("Exception in existsByEmail()",e); throw new RuntimeException(e); } } }
- TokenRefreshResponse.java
package com.security.authmanager.repository; import com.security.authmanager.common.QueryConstants; import com.security.authmanager.model.ERole; import com.security.authmanager.model.RefreshToken; import com.security.authmanager.model.Role; import com.security.authmanager.model.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import java.sql.PreparedStatement; import java.sql.Timestamp; import java.util.Optional; @Repository public class RefreshTokenRepository { private static final Logger logger = LoggerFactory.getLogger(RefreshTokenRepository.class); @Autowired private JdbcTemplate jdbcTemplate; public void deleteRefreshToken(RefreshToken refreshToken) { try{ jdbcTemplate.update(QueryConstants.DELETE_REFRESH_TOKEN,(final PreparedStatement ps) ->{ ps.setString(1,refreshToken.getToken()); }); }catch (Exception e){ logger.error("Exception in deleteRefreshToken()",e); throw new RuntimeException(e); } } public int deleteRefreshTokenByUser(User user) { return 0; } public RefreshToken saveRefreshToken(RefreshToken refreshToken) { try{ jdbcTemplate.update(QueryConstants.SAVE_REFRESH_TOKEN,(final PreparedStatement ps) ->{ ps.setLong(1,refreshToken.getUser().getId()); ps.setString(2,refreshToken.getToken()); ps.setTimestamp(3, Timestamp.from(refreshToken.getExpiryDate())); }); }catch (Exception e){ logger.error("Exception in saveRefreshToken()",e); throw new RuntimeException(e); } return refreshToken; } public Optional<RefreshToken> findByToken(String token) { RefreshToken refreshToken = new RefreshToken(); try{ return Optional.ofNullable(jdbcTemplate.query((conn) -> { final PreparedStatement ps = conn.prepareStatement(QueryConstants.FIND_BY_TOKEN); ps.setString(1, token); return ps; }, rs -> { User user = new User(); while (rs.next()) { refreshToken.setId(rs.getLong("id")); refreshToken.setToken(rs.getString("token")); refreshToken.setExpiryDate(rs.getTimestamp("expiryDate").toInstant()); user.setId(rs.getLong("uniqueid")); user.setEmail(rs.getString("email")); user.setUsername(rs.getString("username")); refreshToken.setUser(user); } return refreshToken; })); }catch(Exception e){ logger.error("Exception in findByToken()",e); throw new RuntimeException(e); } } }
- AuthController.java
The AuthController class is a Spring @RestController responsible for handling authentication-related endpoints in the application. It provides endpoints for user login, registration, and token refresh operations.
Key Functions:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> </dependency>
- TestController.java
The TestController class is a Spring @RestController that provides several endpoints for testing access control based on user roles. It demonstrates how to use Spring Security's role-based authorization to restrict access to certain parts of the application.
Key Functions:
spring.application.name= authmanager server.port= 1001 servlet.context-path= /authmanager database.username= postgres database.password= admin@123 database.driverClassName= org.postgresql.Driver database.jdbcUrl= jdbc:postgresql://localhost:5432/postgres database.maxActive= 5 database.minIdle= 5 database.poolName= Authmanager Postgres Datasource app.jwtSecret= ###############ib-Spring############### app.jwtExpirationMs= 3600000 app.jwtRefreshExpirationMs= 86400000
1. Register as mod and user. (Signin)
2. Login to get access token.
3. Get Refresh Token API.
4. Testing the user access by passing the access token.
5. Testing the mod access by passing the access token.
6. Testing the admin access by passing the same access token.
Unauthorized since this user don't have admin access (user has only mod and user roles)
Happy learning! See you again.?
The above is the detailed content of Implementing Token-Based Authentication in Spring Boot Using Spring Security, JWT, and JDBC Template. For more information, please follow other related articles on the PHP Chinese website!