Integrate the Java JSON Web Token(jjwt) library into Spring Security
Make web API protection by JWT in Spring Security like a breeze
<dependency>
<groupId>com.github.wnameless.spring</groupId>
<artifactId>spring-security-jjwt</artifactId>
<version>0.2.0</version>
</dependency>
Extend AbstractJwtSecurityConfiguration to enable JWT security
AbstractJwtSecurityConfiguration pre-configures all necessary settings including:
- CORS
- CSRF disabled
- JwtAuthenticationFilter
- JwtAuthorizationFilter
- Stateless session
@EnableWebSecurity
public class JwtSecurityConfiguration extends AbstractJwtSecurityConfiguration {
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http); // IMPORTANT!!!
http.antMatcher("/api/**") // Using JWT to protect the API endpoint
.authorizeRequests().anyRequest().authenticated();
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user")
.password(passwordEncoder().encode("password"))
.authorities("ROLE_USER");
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
IMPORTANT: remember to execute super.configure(http) at the first line of #configure(HttpSecurity)
Run test Application & Controller
@SpringBootApplication
public class JwtApplication {
public static void main(String... args) {
SpringApplication.run(JwtApplication.class, args);
}
@RequestMapping("/api/data")
@RestController
public static class DataController {
@GetMapping
public String getData() {
return "Private data";
}
}
}
Test JWT with Axios
var token;
axios.post('http://localhost:8080/api/auth?username=user&password=password')
.then(res => {
token = res.data;
});
axios.get('http://localhost:8080/api/data', { headers: { Authorization: `Bearer ${token}` } })
.then(res => { console.log(res.data) });
By application.properties
# default: /api/auth
jwt.auth-url=/api/login
# 512 bytes at least
jwt.secret=QeThWmZq4t7w!z%C*F-JaNdRgUjXn2r5u8x/A?D(G+KbPeShVmYp3s6v9y$B&E)H
# default: 604800000(7 days)
jwt.expiration=432000000
By @Bean, JwtSecurityProperties bean overides settings in application.properties
@Bean
public JwtSecurityProperties jwtSecurityProperties() {
return new JwtSecurityProperties(
"/api/login",
"QeThWmZq4t7w!z%C*F-JaNdRgUjXn2r5u8x/A?D(G+KbPeShVmYp3s6v9y$B&E)H",
432000000);
}
Since v0.2.0, JWT expiration extending service has been added
The Expiration Extending Service can be enable simply by providing a JwtExpirationExtendingPolicy bean
JwtExpirationExtendingPolicy is called whenever a request JWT is expired
Following example shows how to extend an expired JWT, only if the token was used in past 2 days
@Bean
JwtExpirationExtendingPolicy jwtExpirationExtendingPolicy() {
return (jwtClaims, lastLoginTime) -> {
if (lastLoginTime.isPresent()) {
return new Date().getTime() - lastLoginTime.get()
.getTime() < TimeUnit.MILLISECONDS.convert(2, TimeUnit.DAYS);
} else {
return false;
}
};
}
By default, the JwtExpirationExtendingService wipes out all last-login records which are outdated for 2 weeks
However you can provide your JwtExpirationExtendingService to meet your need in JwtExpirationExtendingPolicy
@Bean
JwtExpirationExtendingService jwtExpirationExtendingService() {
return new MapDBJwtExpirationExtendingService(
TimeUnit.MICROSECONDS.convert(180, TimeUnit.DAYS));
}