Basic security enclosure for JSON REST backends on Spring Boot WebMvc
Json-rest-security-spring-boot-starter is a starter for Spring Boot that sets up a simple security enclosure. Including:
- CORS settings
- Basic and Access-Refresh (JWT+UUID) authentications
- Basic AuthController for authentications
- User entity facades
- Fail limiting and last visit filters
Add the dependency to your pom.xml
<dependency>
<groupId>com.a6raywa1cher</groupId>
<artifactId>json-rest-security-spring-boot-starter</artifactId>
<version>0.0.7</version>
</dependency>
If you want to try and test the starter, just start your application and read "Access-Refresh authorization flow".
However, it isn't a recommended approach.
You can create a user class in two different ways:
- Extend the
AbstractUser
class. This way, all necessary fields will be presented without any additional code. - Implement the
IUser
interface. You could create these fields in your user class to comply with the interface with setters and getters:private Long id; private String userRole; private String password; private List<RefreshToken> refreshTokens; private LocalDateTime lastVisitAt;
Then create UserRepository with the IUserRepository
interface:
@Repository
public interface UserRepository extends IUserRepository<User>, CrudRepository<User, Long> {
}
And finally create UserServiceImpl with the AbstractUserService
superclass:
@Component
public class UserServiceImpl extends AbstractUserService<User> {
public UserServiceImpl(IUserRepository<User> userRepository, PasswordEncoder passwordEncoder) {
super(userRepository, passwordEncoder);
}
@Override
public User create(String login, String rawPassword, String role) {
User user = new User();
user.setUsername(login);
user.setPassword(passwordEncoder.encode(rawPassword));
user.setUserRole(role);
return userRepository.save(user);
}
}
RefreshToken
isn't an Entity in terms of JPA. By default, refresh tokens are stored as a JSON array field in the user
class. You can, however, extend the class and change it.
The simplest way to modify your Spring Security configuration is to extend the JsonRestWebSecurityConfigurer
class. An
example:
@Configuration
public class SecurityConfig extends JsonRestWebSecurityConfigurer {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/home/admin").hasAnyRole("ADMIN");
super.configure(http);
}
}
JsonRestWebSecurityConfigurer
has a couple of options to mention:
- To prevent the usage of
.anyMatcher()
, usesetUseAnyMatcher(false)
- If you need to customize
JsonRestWebSecurityConfigurer
in a special way, you can overridevoid configure(HttpSecurity http)
andvoid configure(AuthenticationManagerBuilder auth)
. Check out the implementation here .
Provide a header with the access token with every request:
Authorization: Basic YWJjZGVmOnF3ZXJ0eQ==
Use GET /auth/user
to get the user's id and username.
- The User sends a request to
POST /auth/login
with body:If the username/password pair is valid, the server will return a pair of tokens:{ "username": "abcdef", "password": "qwerty" }
{ "refreshToken": "8ce451bb-44cb-4fbd-9e9d-02ee3335e176", "refreshTokenExpiringAt": "2022-03-05T12:42:44.212708+03:00", "accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxIiwicnRpIjoiYjU0YjkxNjgtYTMwNC00YTYwLTlkMDctZWNjNzRmYzkzOTM5IiwiaXNzIjoidGVzdC1hcHAiLCJleHAiOjE2NDUyNjQwNjR9.vTU5g7K0j2s6ywD0CAevuMNblKnrmXIUckrIUQ5_LH-Xc8rdc1aD4A-Mvbapqg1r0LuiCV4SEOUw-2HKETX2MQ", "accessTokenExpiringAt": "2022-02-19T12:47:44.220801+03:00", "userId": 1 }
- Provide a header with the access token with every request:
Authorization: Bearer eyJ0eXAiO...
- The user can check if accessToken is still works with
GET /auth/check
. - Check
GET /auth/user
to get the user's id and username:{ "id": 1, "username": "abcdef" }
- If the accessToken is expired, request a new one with the refreshToken at
POST /auth/get_access
with body:The user will get a new pair of tokens.{ "refreshToken": "8ce451bb-44cb-4fbd-9e9d-02ee3335e176", "userId": 1 }
- The user can request
DELETE /auth/invalidate
to invalidate refresh token and associated access token with body:{ "refreshToken": "8ce451bb-44cb-4fbd-9e9d-02ee3335e176", "userId": 1 }
- If user want to make a full logout from all devices, use
DELETE /auth/invalidate_all
.
AuthController is compatible with springdoc-openapi.
Granted authorities of the user are provided by grantedAuthorityService
bean.
The default implementation returns two types of authorities based on SimpleGrantedAuthority
:
ROLE_${userRole}
. For example,ROLE_USER
.ENABLED
if the user is enabled.
The user is enabled if the userEnabledChecker
bean returns true. By default, it's always true.
You are free to implement your grantedAuthorityService
and userEnabledChecker
beans by overriding
GrantedAuthorityService
and UserEnabledChecker
.
Fail-limiter is a submodule of json-rest-security. It prevents password brute-force attacks.
The client will be banned for block-duration
after max-attempts
fails. Failure attempt count is reset after
block-duration
since the last failed request.
The request is failed in terms of fail-limiter if the response code is 401 (Unauthorized) or 403 (Forbidden).
Properties for fail-limiter are listed below.
Property name | Description | Default value |
---|---|---|
json-rest-security.jwt.secret |
JWT token secret key | Generated at each app start |
Property name | Description | Default value |
---|---|---|
json-rest-security.enable |
Enable or disable json-rest-security |
true |
json-rest-security.enable-auth-controller |
Enable or disable AuthController | true |
json-rest-security.enable-default-web-config |
Enable or disable JsonRestWebSecurityConfigurer | true |
json-rest-security.cors-allowed-origins |
CORS allowed origins, array | empty |
json-rest-security.jwt.access-duration |
Access token duration | PT5M |
json-rest-security.jwt.refresh-duration |
Refresh token duration | P14D |
json-rest-security.jwt.max-refresh-tokens-per-user |
Max refresh token count per user | 10 |
json-rest-security.jwt.issuer-name |
Issuer name (presented in JWT tokens) | json-rest-security |
json-rest-security.fail-limiter.enable |
Enable or disable fail-limiter module | true |
json-rest-security.fail-limiter.block-duration |
Client block time after max-attempts fails |
PT1M |
json-rest-security.fail-limiter.max-attempts |
Max attempts to fail before ban on fail-limiter | 5 |
json-rest-security.fail-limiter.max-cache-size |
Max block list size | 30000 |
json-rest-security.first-user.enable |
Enable or disable FirstUserCreatorApplicationListener | true |
json-rest-security.first-user.username |
First user's username | admin |
json-rest-security.first-user.password |
First user's password | admin |
json-rest-security.first-user.role |
First user's role | ADMIN |
You can configure json-rest-security.cors-allowed-origins
with an environment variable
JSON_REST_SECURITY_CORS_ALLOWED_ORIGINS
using comma separated list of allowed origins:
JSON_REST_SECURITY_CORS_ALLOWED_ORIGINS=http://localhost:8080,http://localhost:3000
Any feature requests, improvements, bug reports, and security reports are welcome!