Skip to content

Commit 7d2f8b3

Browse files
authored
Merge pull request #4 from manirDev/jwt-token
JWT token provider is implemented.
2 parents b02e91b + b3e8afd commit 7d2f8b3

File tree

8 files changed

+207
-4
lines changed

8 files changed

+207
-4
lines changed

pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@
5252
<groupId>org.springframework.boot</groupId>
5353
<artifactId>spring-boot-starter-security</artifactId>
5454
</dependency>
55+
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
56+
<dependency>
57+
<groupId>io.jsonwebtoken</groupId>
58+
<artifactId>jjwt</artifactId>
59+
<version>0.9.1</version>
60+
</dependency>
61+
5562
<dependency>
5663
<groupId>org.springframework.boot</groupId>
5764
<artifactId>spring-boot-starter-test</artifactId>

src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.manir.springbootecommercerestapi.config;
22

33
import com.manir.springbootecommercerestapi.security.CustomUserDetailsService;
4+
import com.manir.springbootecommercerestapi.security.JwtAuthenticationEntryPoint;
5+
import com.manir.springbootecommercerestapi.security.JwtAuthenticationFilter;
46
import org.springframework.beans.factory.annotation.Autowired;
57
import org.springframework.context.annotation.Bean;
68
import org.springframework.context.annotation.Configuration;
@@ -11,8 +13,10 @@
1113
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
1214
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
1315
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
16+
import org.springframework.security.config.http.SessionCreationPolicy;
1417
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
1518
import org.springframework.security.crypto.password.PasswordEncoder;
19+
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
1620

1721
@Configuration
1822
@EnableWebSecurity
@@ -25,21 +29,43 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
2529

2630
@Autowired
2731
private CustomUserDetailsService customUserDetailsService;
32+
@Autowired
33+
private JwtAuthenticationEntryPoint authenticationEntryPoint;
2834

2935
@Override
3036
protected void configure(HttpSecurity http) throws Exception {
3137
http
3238
.csrf().disable()
39+
/*-------------------------JWT Starts------------------------------*/
40+
.exceptionHandling()
41+
.authenticationEntryPoint(authenticationEntryPoint)
42+
.and()
43+
.sessionManagement()
44+
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
45+
/*-------------------------JWT ends------------------------------*/
46+
.and()
3347
.authorizeRequests()
3448
//to permit all get request and secure post put and delete methods
3549
.antMatchers(HttpMethod.GET, "/api/**").permitAll()
3650
//authorize singIn and signUp
3751
.antMatchers("/api/v1/auth/**").permitAll()
3852
.anyRequest()
39-
.authenticated()
40-
.and()
53+
.authenticated();
54+
55+
/**
56+
Basic auth used before JWT implementation
57+
.and()
4158
.httpBasic();
59+
**/
60+
http
61+
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
62+
63+
}
4264

65+
//Jwt auth filter method
66+
@Bean
67+
public JwtAuthenticationFilter jwtAuthenticationFilter(){
68+
return new JwtAuthenticationFilter();
4369
}
4470

4571
//In memory Auth

src/main/java/com/manir/springbootecommercerestapi/controller/AuthController.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import com.manir.springbootecommercerestapi.dto.LoginDto;
44
import com.manir.springbootecommercerestapi.dto.SignUpDto;
55
import com.manir.springbootecommercerestapi.repository.UserRepository;
6+
import com.manir.springbootecommercerestapi.response.JWTAuthResponse;
7+
import com.manir.springbootecommercerestapi.security.JwtTokenProvider;
68
import com.manir.springbootecommercerestapi.service.UserRegisterService;
79
import org.springframework.beans.factory.annotation.Autowired;
810
import org.springframework.http.HttpStatus;
@@ -28,10 +30,12 @@ public class AuthController {
2830
private UserRepository userRepository;
2931
@Autowired
3032
private UserRegisterService userRegisterService;
33+
@Autowired
34+
private JwtTokenProvider tokenProvider;
3135

3236
//login api
3337
@PostMapping("/login")
34-
public ResponseEntity<String> authenticateUser(@RequestBody LoginDto loginDto){
38+
public ResponseEntity<JWTAuthResponse> authenticateUser(@RequestBody LoginDto loginDto){
3539

3640
Authentication authentication = authenticationManager.authenticate(
3741
new UsernamePasswordAuthenticationToken(
@@ -40,7 +44,11 @@ public ResponseEntity<String> authenticateUser(@RequestBody LoginDto loginDto){
4044
)
4145
);
4246
SecurityContextHolder.getContext().setAuthentication(authentication);
43-
return new ResponseEntity<>("User sign-In successfully", HttpStatus.OK);
47+
48+
//get token from token provider
49+
String token = tokenProvider.generateToken(authentication);
50+
51+
return new ResponseEntity<>(new JWTAuthResponse(token), HttpStatus.OK);
4452
}
4553

4654
//register api
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.manir.springbootecommercerestapi.response;
2+
3+
import lombok.Getter;
4+
import lombok.Setter;
5+
6+
@Getter
7+
@Setter
8+
public class JWTAuthResponse {
9+
private String accessToken;
10+
private String tokenType = "Bearer";
11+
12+
public JWTAuthResponse(String accessToken) {
13+
this.accessToken = accessToken;
14+
}
15+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.manir.springbootecommercerestapi.security;
2+
3+
import org.springframework.security.core.AuthenticationException;
4+
import org.springframework.security.web.AuthenticationEntryPoint;
5+
import org.springframework.stereotype.Component;
6+
7+
import javax.servlet.ServletException;
8+
import javax.servlet.http.HttpServletRequest;
9+
import javax.servlet.http.HttpServletResponse;
10+
import java.io.IOException;
11+
12+
@Component
13+
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
14+
@Override
15+
public void commence(HttpServletRequest request,
16+
HttpServletResponse response,
17+
AuthenticationException authException) throws IOException, ServletException {
18+
19+
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
20+
}
21+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.manir.springbootecommercerestapi.security;
2+
3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
5+
import org.springframework.security.core.context.SecurityContextHolder;
6+
import org.springframework.security.core.userdetails.UserDetails;
7+
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
8+
import org.springframework.util.StringUtils;
9+
import org.springframework.web.filter.OncePerRequestFilter;
10+
11+
import javax.servlet.FilterChain;
12+
import javax.servlet.ServletException;
13+
import javax.servlet.http.HttpServletRequest;
14+
import javax.servlet.http.HttpServletResponse;
15+
import java.io.IOException;
16+
17+
public class JwtAuthenticationFilter extends OncePerRequestFilter {
18+
19+
@Autowired
20+
private JwtTokenProvider tokenProvider;
21+
@Autowired
22+
private CustomUserDetailsService userDetailsService;
23+
24+
@Override
25+
protected void doFilterInternal(HttpServletRequest request,
26+
HttpServletResponse response,
27+
FilterChain filterChain) throws ServletException, IOException {
28+
29+
//get jwt token from http request
30+
String token = getJWTFromToken(request);
31+
//validate token
32+
if (StringUtils.hasText(token) && tokenProvider.validateToken(token)){
33+
//retrieve user form token
34+
String userName = tokenProvider.getUserNameFromToken(token);
35+
//load user associated with the token
36+
UserDetails userDetails = userDetailsService.loadUserByUsername(userName);
37+
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
38+
userDetails, null, userDetails.getAuthorities()
39+
);
40+
//set spring security
41+
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
42+
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
43+
}
44+
filterChain.doFilter(request, response);
45+
}
46+
47+
//get jwt from token
48+
//Bearer <accessToken>
49+
private String getJWTFromToken(HttpServletRequest request){
50+
51+
String bearerToken = request.getHeader("Authorization");
52+
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")){
53+
return bearerToken.substring(7, bearerToken.length());
54+
}
55+
return null;
56+
}
57+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.manir.springbootecommercerestapi.security;
2+
3+
import com.manir.springbootecommercerestapi.exception.EcommerceApiException;
4+
import io.jsonwebtoken.*;
5+
import org.springframework.beans.factory.annotation.Value;
6+
import org.springframework.http.HttpStatus;
7+
import org.springframework.security.core.Authentication;
8+
import org.springframework.stereotype.Component;
9+
10+
import java.util.Date;
11+
12+
@Component
13+
public class JwtTokenProvider {
14+
15+
@Value("${app.jwt-secret}")
16+
private String jwtSecret;
17+
@Value("${app.jwt-expiration-milliseconds}")
18+
private int jwtExpirationTime;
19+
20+
//generate token method
21+
22+
public String generateToken(Authentication authentication){
23+
String userName = authentication.getName();
24+
Date currentDate = new Date();
25+
Date expireDate = new Date(currentDate.getTime() + jwtExpirationTime);
26+
27+
String token = Jwts.builder()
28+
.setSubject(userName)
29+
.setIssuedAt(new Date())
30+
.setExpiration(expireDate)
31+
.signWith(SignatureAlgorithm.HS512, jwtSecret)
32+
.compact();
33+
34+
return token;
35+
}
36+
37+
//get username from the token, retrieve username from generated token
38+
public String getUserNameFromToken(String token){
39+
Claims claims = Jwts.parser()
40+
.setSigningKey(jwtSecret)
41+
.parseClaimsJws(token)
42+
.getBody();
43+
44+
return claims.getSubject();
45+
}
46+
47+
//validate JWT token
48+
public boolean validateToken(String token){
49+
try {
50+
Jwts.parser()
51+
.setSigningKey(jwtSecret)
52+
.parseClaimsJws(token);
53+
return true;
54+
}catch (SignatureException e){
55+
throw new EcommerceApiException("Invalid JWT signature", HttpStatus.BAD_REQUEST);
56+
}catch (MalformedJwtException e){
57+
throw new EcommerceApiException("Invalid JWT token", HttpStatus.BAD_REQUEST);
58+
}catch (ExpiredJwtException e){
59+
throw new EcommerceApiException("Expired JWT token", HttpStatus.BAD_REQUEST);
60+
}catch (UnsupportedJwtException e){
61+
throw new EcommerceApiException("Unsupported JWT token", HttpStatus.BAD_REQUEST);
62+
}catch (IllegalArgumentException e){
63+
throw new EcommerceApiException("JWT claims string is empty", HttpStatus.BAD_REQUEST);
64+
}
65+
}
66+
}

src/main/resources/application.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@ logging.level.org.springframework.security=DEBUG
1313
#spring.security.customer.name= customer
1414
#spring.security.customer.roles= ADMIN
1515

16+
#JWT properties
17+
app.jwt-secret = JWTSecretKey
18+
app.jwt-expiration-milliseconds = 604800000

0 commit comments

Comments
 (0)