Bootstrapping your Application with InnoBridge Security
Motivation
InnoBridge Security simplifies the implementation of security in your application by reducing the need for extensive boilerplate code. It enables you to easily bootstrap security features such as username, email, password, JWT, and OAuth2 support, as described in Securing Java Applications with Spring Security, JWT, and OpenID , by simply configuring a SecurityConfig file and setting a few environment variables.
Features
Prerequisites
Installation
Add the InnoBridge Security dependency to your pom.xml:
<dependency>
<groupId>io.github.innobridge</groupId>
<artifactId>security</artifactId>
<version>0.0.1</version>
</dependency>
Username, email, password and JWT authentication
Set the following environment variables:
export MONGO_DATABASE_URI=<your-mongo-database-uri>
export JWT_ACCESS_SIGNING_KEY=<your-jwt-access-signing-key>
export JWT_REFRESH_SIGNING_KEY=<your-jwt-refresh-signing-key>
Create the configuration file SecurityConfig.java
@Configuration
@EnableWebSecurity
@Import(InnoBridgeSecurityConfig.class)
@EnableMongoRepositories(basePackages = {
"io.github.innobridge.security.repository",
<Location of your Mongo Repository>, // eg. "io.yilengyao.jwtauth.repository"
})
public class SecurityConfig implements WebMvcConfigurer {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http,
UsernamePasswordAuthenticationFilter usernameEmailPasswordAuthenticationFilter,
UsernameEmailPasswordRegistrationFilter usernameEmailPasswordRegistrationFilter,
JwtAuthenticationFilter jwtAuthenticationFilter,
RefreshTokenFilter refreshTokenFilter,
LogoutFilter logoutFilter) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(
authorize -> authorize
.requestMatchers(WHITE_LIST_URL).permitAll()
.anyRequest().authenticated()
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) )
.oauth2Login(oauth2 ->
oauth2.clientRegistrationRepository(clientRegistrationRepository)// Ensure OAuth2 login is configured
.successHandler(customOAuth2SuccessHandler))
.addFilterAt(usernameEmailPasswordAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(usernameEmailPasswordRegistrationFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(refreshTokenFilter, JwtAuthenticationFilter.class)
.addFilterAfter(logoutFilter, RefreshTokenFilter.class);
return http.build();
}
The default Endpoints:
To access protected endpoints the user needs to pass the access token in the Authorization header with the scheme Bearer.
领英推荐
Override default configuration
You can override the default configuration by setting the following properties:
eg.
@Configuration
@EnableWebSecurity
@Import(InnoBridgeSecurityConfig.class)
@EnableMongoRepositories(basePackages = {
"io.github.innobridge.security.repository",
<Location of your Mongo Repository>, // eg. "io.yilengyao.jwtauth.repository"
})
public class SecurityConfig implements WebMvcConfigurer {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http,
UsernamePasswordAuthenticationFilter usernameEmailPasswordAuthenticationFilter,
UsernameEmailPasswordRegistrationFilter usernameEmailPasswordRegistrationFilter,
JwtAuthenticationFilter jwtAuthenticationFilter,
RefreshTokenFilter refreshTokenFilter,
LogoutFilter logoutFilter) throws Exception {
jwtUtils.setAccessTokenExpiration(new ExpirationTime(0, 2, 0, 0));
jwtUtils.setRefreshTokenExpiration(new ExpirationTime(7, 0, 0, 0));
usernameEmailPasswordAuthenticationFilter.setFilterProcessesUrl("/auth/login");
usernameEmailPasswordRegistrationFilter.setUrl("/auth/register");
jwtAuthenticationFilter.setSignoutUrl("/auth/logout");
jwtAuthenticationFilter.setRefreshTokenUrl("/auth/tokenrefesh");
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(
authorize -> authorize
.requestMatchers(WHITE_LIST_URL).permitAll()
.anyRequest().authenticated()
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) )
.oauth2Login(oauth2 ->
oauth2.clientRegistrationRepository(clientRegistrationRepository)// Ensure OAuth2 login is configured
.successHandler(customOAuth2SuccessHandler))
.addFilterAt(usernameEmailPasswordAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(usernameEmailPasswordRegistrationFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(refreshTokenFilter, JwtAuthenticationFilter.class)
.addFilterAfter(logoutFilter, RefreshTokenFilter.class);
return http.build();
}
}
[Optional] Controller to scaffold OpenAPI
The following controller isn’t required and does not provide any functionality, it provides scaffolding for the OpenAPI spec. You can access the OpenAPI spec at https://localhost:8080/swagger-ui/index.html if you are running locally. Or https://<your-domain>/swagger-ui/index.html if you are running in production.
@Slf4j
@RestController
public class AuthenticationController {
@Autowired
private UserService userService;
@Autowired
private JwtUtils jwtUtils;
@PostMapping(SIGNUP_URL)
@ApiResponses(value = {
@ApiResponse(responseCode = CREATED,
description = "Successful signup",
content = @Content(mediaType = CONTENT_TYPE,
schema = @Schema(implementation = SignupResponse.class)))
})
public ResponseEntity<?> registerUser(@RequestBody SignupRequest signupRequest, HttpServletResponse response) {
return ResponseEntity.ok(response);
}
/**
* When the username/email and password are validated during signin, an access token and refresh token are returned
* to the user. The access token is used to authenticate the user for a short period of time, so it is passed back to
* user in the response body along with the expiry time.
* While the refresh token are longer lived and are stored in an HTTP-only cookie on the user's browser.
*/
@PostMapping(SIGNIN_URL)
@ApiResponses(value = {
@ApiResponse(responseCode = CREATED, description = "Successful signin",
content = @Content(mediaType = CONTENT_TYPE,
schema = @Schema(implementation = AccessTokenResponse.class)))
})
public ResponseEntity<?> authenticateUser(@RequestBody SigninRequest signinRequest, HttpServletResponse response) {
return ResponseEntity.ok(response);
}
@PostMapping(REFRESH_TOKEN_URL)
@ApiResponses(value = {
@ApiResponse(responseCode = CREATED, description = "Refresh token successful",
content = @Content(mediaType = CONTENT_TYPE,
schema = @Schema(implementation = AccessTokenResponse.class)))
})
public ResponseEntity<?> refreshToken(HttpServletResponse response) {
return ResponseEntity.ok(response);
}
@PostMapping(SIGNOUT_URL)
public ResponseEntity<?> logoutUser(HttpServletResponse response) {
return ResponseEntity.ok(response);
}
}
OpenID Connect Authentication
Requirements
Set the following environment variables:
export MONGO_DATABASE_URI=<your-mongo-database-uri>
export JWT_ACCESS_SIGNING_KEY=<your-jwt-access-signing-key>
export JWT_REFRESH_SIGNING_KEY=<your-jwt-refresh-signing-key>
export GOOGLE_CLIENT_ID=<your-google-client-id>
export GOOGLE_CLIENT_SECRET=<your-google-client-secret>
export OAUTH2_REDIRECT_BASE_URI=<localhost:8080 if developing locally or your-domain>
Create the configuration file SecurityConfig.java
@Configuration
@EnableWebSecurity
@Import(InnoBridgeSecurityConfig.class)
@EnableMongoRepositories(basePackages = {
"io.github.innobridge.security.repository",
<Location of your Mongo Repository>, // eg. "io.yilengyao.jwtauth.repository"
})
public class SecurityConfig implements WebMvcConfigurer {
@Bean
public ClientRegistrationRepository clientRegistrationRepository(ClientRegistration clientRegistration) {
return new InMemoryClientRegistrationRepository(clientRegistration);
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http,
UsernamePasswordAuthenticationFilter usernameEmailPasswordAuthenticationFilter,
UsernameEmailPasswordRegistrationFilter usernameEmailPasswordRegistrationFilter,
JwtAuthenticationFilter jwtAuthenticationFilter,
RefreshTokenFilter refreshTokenFilter,
LogoutFilter logoutFilter,
CustomOAuth2SuccessHandler customOAuth2SuccessHandler,
ClientRegistrationRepository clientRegistrationRepository) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(
authorize -> authorize
.requestMatchers(WHITE_LIST_URL).permitAll()
.anyRequest().authenticated()
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // Default to stateless
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) // Stateful for OAuth2 flows
.sessionFixation().none() // No session fixation protection
)
.oauth2Login(oauth2 ->
oauth2.clientRegistrationRepository(clientRegistrationRepository)// Ensure OAuth2 login is configured
.successHandler(customOAuth2SuccessHandler))
.addFilterAt(usernameEmailPasswordAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(usernameEmailPasswordRegistrationFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(refreshTokenFilter, JwtAuthenticationFilter.class)
.addFilterAfter(logoutFilter, RefreshTokenFilter.class);
return http.build();
}
}
The user can login via Google Oauth2 by calling the URL:
Source Code
Github Repo of InnoBridge Security .