Skip to content

Commit ce9109a

Browse files
committed
fixing refresh token bugs
1 parent e419924 commit ce9109a

13 files changed

+111
-46
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ target/
33
!.mvn/wrapper/maven-wrapper.jar
44
!**/src/main/**/target/
55
!**/src/test/**/target/
6-
6+
src/main/resources/application.yml
77
### STS ###
88
.apt_generated
99
.classpath

pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@
4040
<scope>runtime</scope>
4141
<optional>true</optional>
4242
</dependency>
43+
<dependency>
44+
<groupId>org.springframework.boot</groupId>
45+
<artifactId>spring-boot-configuration-processor</artifactId>
46+
<optional>true</optional>
47+
</dependency>
4348
<dependency>
4449
<groupId>com.mysql</groupId>
4550
<artifactId>mysql-connector-j</artifactId>
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
package com.admi.jwtauthenticationspringsecurity;
22

3+
import org.springframework.beans.factory.annotation.Value;
34
import org.springframework.boot.SpringApplication;
45
import org.springframework.boot.autoconfigure.SpringBootApplication;
56

67
@SpringBootApplication
78
public class JwtAuthenticationSpringSecurityApplication {
89

10+
@Value("db.username")
11+
String ss;
12+
913
public static void main(String[] args) {
10-
SpringApplication.run(JwtAuthenticationSpringSecurityApplication.class, args);
14+
// SpringApplication.run(JwtAuthenticationSpringSecurityApplication.class, args);
15+
JwtAuthenticationSpringSecurityApplication application = new JwtAuthenticationSpringSecurityApplication();
16+
System.out.println(application.ss);
1117
}
1218

1319
}

src/main/java/com/admi/jwtauthenticationspringsecurity/configurations/SecurityConfiguration.java

+7-3
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
import com.admi.jwtauthenticationspringsecurity.security.CustomeUserDetailsService;
66
import org.springframework.context.annotation.Bean;
77
import org.springframework.context.annotation.Configuration;
8+
import org.springframework.http.HttpMethod;
89
import org.springframework.security.authentication.AuthenticationManager;
910
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
1011
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
1112
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
1213
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
14+
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
1315
import org.springframework.security.config.http.SessionCreationPolicy;
1416
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
1517
import org.springframework.security.crypto.password.PasswordEncoder;
@@ -35,19 +37,21 @@ public SecurityFilterChain securityFilterChain (HttpSecurity httpSecurity) throw
3537
authenticationManager = builder.build();
3638
return
3739
httpSecurity
38-
.cors(httpSecurityCorsConfigurer -> httpSecurityCorsConfigurer.disable())
40+
.cors(AbstractHttpConfigurer::disable)
41+
.csrf(AbstractHttpConfigurer::disable)
3942
.sessionManagement(httpSecuritySessionManagementConfigurer -> httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
4043
.headers(httpSecurityHeadersConfigurer -> httpSecurityHeadersConfigurer.frameOptions().disable())
4144
.authorizeHttpRequests(
4245
authorizationManagerRequestMatcherRegistry -> authorizationManagerRequestMatcherRegistry
43-
.antMatchers("").permitAll()
46+
.antMatchers(HttpMethod.POST,"/register/user").permitAll()
47+
.antMatchers("/token/**","/login/**").permitAll()
4448
.anyRequest().authenticated())
4549
.authenticationManager(authenticationManager)
4650
.addFilter(new JwtAuthenticationFilter(authenticationManager))
4751
.addFilterBefore(new JwtAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class)
4852
.build();
4953

50-
}
54+
}
5155
@Bean
5256
public PasswordEncoder passwordEncoder(){
5357
return new BCryptPasswordEncoder();

src/main/java/com/admi/jwtauthenticationspringsecurity/controllers/RestController.java

+10-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import com.auth0.jwt.algorithms.Algorithm;
1111
import com.auth0.jwt.interfaces.DecodedJWT;
1212
import com.fasterxml.jackson.databind.ObjectMapper;
13+
import org.slf4j.Logger;
14+
import org.slf4j.LoggerFactory;
1315
import org.springframework.security.core.GrantedAuthority;
1416
import org.springframework.security.crypto.password.PasswordEncoder;
1517
import org.springframework.web.bind.annotation.*;
@@ -24,7 +26,7 @@
2426
@org.springframework.web.bind.annotation.RestController
2527
@CrossOrigin
2628
public class RestController {
27-
29+
private Logger logger = LoggerFactory.getLogger(this.getClass());
2830
private UserService userService;
2931
private RoleService roleService;
3032
private PasswordEncoder passwordEncoder;
@@ -35,29 +37,35 @@ public RestController(UserService userService, RoleService roleService, Password
3537
this.passwordEncoder = passwordEncoder;
3638
}
3739

38-
@PostMapping("/user")
40+
@PostMapping("/register/user")
3941
public CustomUser addUser(@RequestBody CustomUser user){
4042
user.setPassword(passwordEncoder.encode(user.getPassword()));
43+
logger.info("registering a user : \n{}",user);
4144
return userService.saveUser(user);
4245
}
4346
@PostMapping("/role")
4447
public CustomRole addRole(@RequestBody CustomRole role){
48+
logger.info("adding new role : {}",role);
4549
return roleService.saveRole(role);
4650
}
4751
@PostMapping("/user/role")
4852
public void addRoleToUser(@RequestBody RoleToUser roleToUser){
53+
logger.info("adding a role to a user {} -> {}",roleToUser.getRole(),roleToUser.getUser());
4954
userService.addRoleToUser(roleToUser.getUser(),roleToUser.getRole());
5055
}
5156
@GetMapping("/user")
5257
public List<CustomUser> listUsers (){
58+
logger.info("listing the users");
5359
return userService.listUsers();
5460
}
5561
@GetMapping("/user/{id}")
5662
public CustomUser getUserById(@PathVariable String id){
63+
logger.info("getting the user with the id = {}",id);
5764
return userService.loadUserById(Long.parseLong(id)).orElse(null);
5865
}
5966
@GetMapping("/token/refresh")
6067
public void requestAccessToken(HttpServletRequest request,HttpServletResponse response){
68+
logger.info("requesting to refresh the token");
6169
String authorizationHeader = request.getHeader("Authorization");
6270
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
6371
try {
@@ -74,8 +82,6 @@ public void requestAccessToken(HttpServletRequest request,HttpServletResponse re
7482
request.getRequestURI().toString(),
7583
ACCESS_TOKEN_EXPIRATION_TIME
7684
);
77-
78-
7985
// String newAccessToken = JWT.create()
8086
// .withSubject(user.getUsername())
8187
// .withExpiresAt(new Date(System.currentTimeMillis()+ACCESS_TOKEN_EXPIRATION_TIME))

src/main/java/com/admi/jwtauthenticationspringsecurity/entities/CustomRole.java

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import lombok.NoArgsConstructor;
66

77
import javax.persistence.*;
8+
import java.util.ArrayList;
9+
import java.util.List;
810

911
@Entity
1012
@Data
@@ -19,5 +21,8 @@ public class CustomRole {
1921
@Column(name = "role_name")
2022
private String roleName;
2123

24+
@ManyToMany(mappedBy = "roles")
25+
private List<CustomUser> userList = new ArrayList<>();
26+
2227

2328
}

src/main/java/com/admi/jwtauthenticationspringsecurity/entities/CustomUser.java

+10-5
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
import lombok.NoArgsConstructor;
77

88
import javax.persistence.*;
9-
import java.util.ArrayList;
10-
import java.util.Collection;
9+
import java.util.*;
1110

1211
@Data
1312
@Entity
@@ -16,17 +15,23 @@
1615
public class CustomUser {
1716
@Id
1817
@GeneratedValue(strategy = GenerationType.IDENTITY)
19-
@Column(name = "id", nullable = false)
18+
@Column(name = "user_id", nullable = false)
2019
private Long id;
2120
@Column(nullable = false)
2221
private String username;
2322
@Column(nullable = false)
24-
//this property ignore the password to be existed in the json response
2523
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
2624
private String password;
2725
@Column
2826
@ManyToMany(fetch = FetchType.EAGER)
29-
Collection<CustomRole> roles = new ArrayList<>();
27+
@JoinTable
28+
(
29+
name = "user_role_table",
30+
joinColumns = {@JoinColumn(name = "user_id",referencedColumnName = "user_id")},
31+
inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "role_id")}
32+
)
33+
Set<CustomRole> roles = new HashSet<>();
34+
3035

3136

3237
}

src/main/java/com/admi/jwtauthenticationspringsecurity/repositories/RoleRepository.java

+2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import com.admi.jwtauthenticationspringsecurity.entities.CustomRole;
44
import org.springframework.data.jpa.repository.JpaRepository;
5+
import org.springframework.stereotype.Repository;
56

7+
@Repository
68
public interface RoleRepository extends JpaRepository<CustomRole,Long> {
79
CustomRole findCustomRoleByRoleName(String roleName);
810
}

src/main/java/com/admi/jwtauthenticationspringsecurity/security/JwtAuthorizationFilter.java

+26-20
Original file line numberDiff line numberDiff line change
@@ -5,46 +5,52 @@
55
import com.auth0.jwt.JWTVerifier;
66
import com.auth0.jwt.algorithms.Algorithm;
77
import com.auth0.jwt.interfaces.DecodedJWT;
8-
import org.springframework.beans.factory.annotation.Value;
8+
import org.slf4j.Logger;
9+
import org.slf4j.LoggerFactory;
910
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
1011
import org.springframework.security.core.GrantedAuthority;
1112
import org.springframework.security.core.authority.SimpleGrantedAuthority;
1213
import org.springframework.security.core.context.SecurityContextHolder;
13-
import org.springframework.security.core.userdetails.User;
1414
import org.springframework.web.filter.OncePerRequestFilter;
1515

1616
import javax.servlet.FilterChain;
1717
import javax.servlet.ServletException;
1818
import javax.servlet.http.HttpServletRequest;
1919
import javax.servlet.http.HttpServletResponse;
2020
import java.io.IOException;
21-
import java.security.SignatureException;
2221
import java.util.ArrayList;
2322
import java.util.Arrays;
2423
import java.util.Collection;
2524

2625
public class JwtAuthorizationFilter extends OncePerRequestFilter {
26+
27+
private Logger logger = LoggerFactory.getLogger(this.getClass());
28+
2729
@Override
2830
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
2931
String authorizationHeader = request.getHeader("Authorization");
30-
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
31-
try {
32-
String accessToken = authorizationHeader.substring(7);
33-
Algorithm algorithm = Algorithm.HMAC256(SecurityUtils.HMAC_KEY);
34-
JWTVerifier verifier = JWT.require(algorithm).build();
35-
DecodedJWT jwt = verifier.verify(accessToken);
36-
String username = jwt.getSubject();
37-
String roles[] = jwt.getClaim("roles").asArray(String.class);
38-
Collection<GrantedAuthority> authorities = new ArrayList<>();
39-
Arrays.stream(roles).forEach(role -> authorities.add(new SimpleGrantedAuthority(role)));
40-
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, null, authorities);
41-
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
32+
if(!SecurityUtils.verifyPath(request.getServletPath())){
33+
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
34+
logger.info("a Bearer container request ");
35+
try {
36+
String accessToken = authorizationHeader.substring(7);
37+
Algorithm algorithm = Algorithm.HMAC256(SecurityUtils.HMAC_KEY);
38+
JWTVerifier verifier = JWT.require(algorithm).build();
39+
DecodedJWT jwt = verifier.verify(accessToken);
40+
String username = jwt.getSubject();
41+
String roles[] = jwt.getClaim("roles").asArray(String.class);
42+
Collection<GrantedAuthority> authorities = new ArrayList<>();
43+
Arrays.stream(roles).forEach(role -> authorities.add(new SimpleGrantedAuthority(role)));
44+
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, null, authorities);
45+
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
46+
filterChain.doFilter(request, response);
47+
} catch (Exception e) {
48+
logger.error("the token is expired");
49+
response.setHeader("error", "the token is expired");
50+
response.sendError(HttpServletResponse.SC_FORBIDDEN);
51+
}
52+
} else
4253
filterChain.doFilter(request, response);
43-
} catch (Exception e) {
44-
logger.error("the token is expired");
45-
response.setHeader("error","the token is expired");
46-
response.sendError(HttpServletResponse.SC_FORBIDDEN);
47-
}
4854
}else
4955
filterChain.doFilter(request,response);
5056

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.admi.jwtauthenticationspringsecurity.utils;
2+
3+
import lombok.Data;
4+
import org.springframework.boot.context.properties.ConfigurationProperties;
5+
import org.springframework.context.annotation.Configuration;
6+
7+
@ConfigurationProperties("database")
8+
@Configuration
9+
@Data
10+
public class DataBaseProperty {
11+
private String username;
12+
private String password;
13+
private String url;
14+
15+
}

src/main/java/com/admi/jwtauthenticationspringsecurity/utils/SecurityUtils.java

+13-7
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,16 @@
55
import com.auth0.jwt.algorithms.Algorithm;
66
import org.springframework.security.core.GrantedAuthority;
77

8-
import java.util.Collection;
9-
import java.util.Collections;
10-
import java.util.Date;
11-
import java.util.List;
8+
import java.util.*;
129

1310
public class SecurityUtils {
1411
public static final String HMAC_KEY = "#YOUR$KEY@HERE!";
15-
public static final int ACCESS_TOKEN_EXPIRATION_TIME = 5 * 60 * 1000;
16-
public static final int REFRESH_TOKEN_EXPIRATION_TIME = 30 * 24 * 3600 * 1000;
12+
public static final long ACCESS_TOKEN_EXPIRATION_TIME = 5 * 60 * 1000;
13+
public static final long REFRESH_TOKEN_EXPIRATION_TIME = 10 * 24 * 3600 * 1000;
14+
public static final long REMEMBER_ME_REFRESH_TOKEN_EXPIRATION_TIME = 20 * 24 * 3600 * 1000;
15+
private static List<String> ignoredPaths = List.of("/token/refresh","/login","/register/user");
1716

18-
public static String generateToken (String username, List<String> authorities, String issuer,int expiredAt){
17+
public static String generateToken (String username, List<String> authorities, String issuer,long expiredAt){
1918
JWTCreator.Builder token = JWT.create()
2019
.withSubject(username)
2120
.withExpiresAt(new Date(System.currentTimeMillis()+expiredAt))
@@ -25,4 +24,11 @@ public static String generateToken (String username, List<String> authorities, S
2524
return token.sign(Algorithm.HMAC256(HMAC_KEY));
2625
return token.withClaim("roles",authorities).sign(Algorithm.HMAC256(HMAC_KEY));
2726
}
27+
28+
public static boolean verifyPath(String path) {
29+
for(String p : ignoredPaths)
30+
if(p.equals(path))
31+
return true;
32+
return false;
33+
}
2834
}
+5-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
spring.datasource.url=jdbc:mysql://localhost:3306/auth
2-
spring.datasource.username=root
3-
spring.datasource.password=@Dmi2020
1+
spring.datasource.url=${database.url}
2+
spring.datasource.username=${database.username}
3+
spring.datasource.password=${database.password}
44
spring.jpa.hibernate.ddl-auto=update
5-
spring.jpa.show-sql=true
5+
spring.jpa.show-sql=false
66
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
7-
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
7+
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
88
spring.jpa.properties.hibernate.format_sql=true

src/main/resources/application.yml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
database:
2+
url: jdbc:mysql://localhost:3306/auth
3+
username: root
4+
password: @Dmi2020
5+
#you can also inject the class as a bean and user the fields to get the values from this property file

0 commit comments

Comments
 (0)