I am currently working on a spring boot project that has multiple security (authentication) configurations combined:
a rest api with http basic auth
a rest api with jwt auth.
a web (form login) with 2fa auth.
The problem I am experiencing is, that the configurations cannot be entirely seperated. More specific: The authenticiation providers are accumulated (in the provider manager), which prevents my preferred setup from working correctly.
What happens is this: In config (3) i have a custom 2fa authentication provider which checks if the credentials (both password and 2FA code!) are entered correctly. If the password is correct, but the 2fa code is not, it exits (catches) with a (authentication or bad credentials) exception. However, as it exits the 2fa authentication provider, it goes back to the provider manager. The latter has another (DAO) auth provider at hand, and checks the credentials again: but this time: only password! not the 2fa code!). As the password is ok, it authorized the request.
So for short: I am experiencing a problem where the first authentication provider does NOT authorize, but the second does (because that one does not take the 2fa code into account)!
I have been at this for days, and i cannot seem to get this right.
As an alternative i have now opted for a solution using a 2fa custom filter. But is not my preferred solution, as it gives me some frontend problems (i first have to authorize the username/password, and only after that i can check the 2fa code).
Is there a solution using my 2fa auth provider? I would sort of wish that the auth providers would not get accumulated, so that when the first auth. provider exits with bad credentials, the auth procedure ends with a 'bad credentials'.
My config class:
WebSecurityConfig.java
#EnableWebSecurity
#Configuration
#AllArgsConstructor
public class WebSecurityConfig_backup extends WebSecurityConfigurerAdapter {
private UserDetailsService userDetailsService;
private PasswordEncoder passwordEncoder;
private HttpBasicAuthenticationExceptionHandler authenticationExceptionHandler;
private JwtAuthenticationProvider jwtAuthenticationProvider;
private MfaAuthenticationDetailsSource mfaAuthenticationDetailsSource;
private MfaAuthenticationProvider2 mfaAuthenticationProvider;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Configuration
#Order(1)
public class ApiV1WebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/v1/**")
.httpBasic()
.authenticationEntryPoint(authenticationExceptionHandler)
.and()
.csrf().disable()
.authorizeRequests(authorize -> {
authorize
.mvcMatchers(HttpMethod.GET,
"/api/v1/transaction/**").hasAnyRole("BANK", "ADMIN")
.mvcMatchers(HttpMethod.POST,
"/api/v1/transaction/**").hasAnyRole("BANK", "ADMIN");
//.anyRequest().denyAll();
})
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth .userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder);
}
}
#Configuration
#Order(2)
public class ApiV2WebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/v2/**")
.addFilter(bearerTokenAuthenticationFilter())
.addFilter(credentialsAuthenticationFilter())
.csrf().disable()
.authorizeRequests(authorize -> {
authorize
.mvcMatchers(HttpMethod.GET,
"/api/v2/transaction/**").hasAnyRole("BANK", "ADMIN")
.mvcMatchers(HttpMethod.POST,
"/api/v2/transaction/**").hasAnyRole("BANK", "ADMIN");
//.anyRequest().denyAll();
})
.authorizeRequests()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
private BearerTokenAuthenticationFilter bearerTokenAuthenticationFilter() throws Exception {
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(authenticationManager());
filter.setAuthenticationFailureHandler(authenticationFailureHandler());
return filter;
}
private CredentialsAuthenticationFilter credentialsAuthenticationFilter() throws Exception {
CredentialsAuthenticationFilter filter = new CredentialsAuthenticationFilter(authenticationManager());
filter.setAuthenticationFailureHandler(authenticationFailureHandler());
return filter;
}
private AuthenticationFailureHandler authenticationFailureHandler() {
return (request, response, ex) -> {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType(MediaType.APPLICATION_JSON.toString());
response.setCharacterEncoding(StandardCharsets.UTF_8.displayName());
ResponseWriterUtil.writeErrorResponse(response, ex.getMessage());
};
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(jwtAuthenticationProvider)
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder);
}
}
#Configuration
#Order(3)
public class FormWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
//authorisation
http//.addFilterBefore(googleTfaFilter, SessionManagementFilter.class)
.authorizeRequests(authorize -> {
authorize
.mvcMatchers("/", "/login", "/logout", "/registrationPage", "/register").permitAll()
.mvcMatchers(HttpMethod.GET,
"/others1", "/others2").hasAnyRole("USER", "ADMIN")
.mvcMatchers(HttpMethod.POST,
"/others1", "/others2").hasAnyRole("USER", "ADMIN");
})
.formLogin()
.loginPage("/login").permitAll()
.usernameParameter("email")
.loginProcessingUrl("/authenticate")
.defaultSuccessUrl("/")
.failureUrl("/login?error")
.authenticationDetailsSource(mfaAuthenticationDetailsSource)
.and()
.logout()
.deleteCookies("JSESSIONID")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET"))
.logoutSuccessUrl("/login?logout").permitAll()
.and()
.sessionManagement()
.sessionFixation().migrateSession()
.and()
.headers().frameOptions().sameOrigin()
.and()
.csrf();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/js/**", "/img/**", "/lib/**");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(mfaAuthenticationProvider)
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder);
}
}
}
auth provider for config 2:
#Slf4j
#RequiredArgsConstructor
#Component
public class JwtAuthenticationProvider implements AuthenticationProvider {
private final UserDetailsService userDetailsService;
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(BearerTokenAuthenticationToken.class);
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
BearerTokenAuthenticationToken bearerToken = (BearerTokenAuthenticationToken) authentication;
Authentication auth = null;
try {
//validate the token
Jwts.parser().setSigningKey(JWT_TOKEN_SECRET).parseClaimsJws(bearerToken.getToken());
JwtTokenUtil jwtTokenUtil = new JwtTokenUtil(bearerToken.getToken());
String username = jwtTokenUtil.getUsernameFromToken();
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
auth = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
log.debug("Authentication token: " + auth);
} catch (IllegalArgumentException e) {
throw new UserServiceAuthenticationException("Invalid token");
} catch (ExpiredJwtException e) {
throw new UserServiceAuthenticationException("Token expired");
} catch (SignatureException e) {
throw new UserServiceAuthenticationException("Invalid signature");
}
return auth;
}
}
Auth provider for config (3)
#Slf4j
#AllArgsConstructor
#Component
public class MfaAuthenticationProvider2 implements AuthenticationProvider {
private UserRepo userRepository;
private GoogleAuthenticator googleAuthenticator;
private PasswordEncoder passwordEncoder;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
String verficationCode = ((MfaAuthenticationDetails) authentication.getDetails()).getUserMFaCode();
User user = userRepository.findByEmail(authentication.getName()).stream().findFirst().orElse(null);
if(user == null || !passwordEncoder.matches(password, user.getPassword())){
throw new BadCredentialsException("Invalid username or password");
}
try {
if(!googleAuthenticator.authorizeUser(user.getUsername(), Integer.parseInt(verficationCode))){
throw new BadCredentialsException("Invalid verification code.");
}
} catch (Exception e) {
throw new BadCredentialsException("Authentication failed. Please try again.");
}
return new UsernamePasswordAuthenticationToken(user, password, user.getAuthorities());
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
My current alternative solution for config 3:
#Slf4j
#Component
public class GoogleTfaFilter2 extends OncePerRequestFilter {
private final AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
private final GoogleTfaFailureHandler googleTfaFailureHandler = new GoogleTfaFailureHandler();
private final RequestMatcher urlIs2fa = new AntPathRequestMatcher("/verify2fa");
private final RequestMatcher urlIs2fa2 = new AntPathRequestMatcher("/register2fa");
private final RequestMatcher urlResource = new AntPathRequestMatcher("/resources/**");
private final RequestMatcher api = new AntPathRequestMatcher("/api/**");
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
StaticResourceRequest.StaticResourceRequestMatcher staticResourceRequestMatcher =
PathRequest.toStaticResources().atCommonLocations();
if (urlIs2fa.matches(request) || urlResource.matches(request) || urlIs2fa2.matches(request)||
staticResourceRequestMatcher.matcher(request).isMatch() || api.matches(request)) {
filterChain.doFilter(request, response);
return;
}
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && !authenticationTrustResolver.isAnonymous(authentication)){
log.debug("Processing 2FA Filter");
if (authentication.getPrincipal() != null && authentication.getPrincipal() instanceof User) {
User user = (User) authentication.getPrincipal();
if (!user.getMfaPassed()) {
log.debug("2FA Required");
request.getRequestDispatcher("/verify2fa").forward(request, response);
return;
}
}
}
filterChain.doFilter(request, response);
}
}
Related
I am trying to implement JWT security. Somewhere, service is being called before controller with null value #RequestBody.
Below are the codes.
WebSecurityConfig.java
protected void configure(HttpSecurity http) throws Exception
{
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/authenticate")
.permitAll()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception{
return super.authenticationManagerBean();
}
#Service
public class JWTUserDetailsService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if("jailhouse".equals(username))
return new User("jailhouse","jailhouse",new ArrayList<>());
else {
throw new BadCredentialsException("User not found with Username: "+username);
}
}
}
#Controller
#PostMapping("/authenticate")
public JWTResponse authenticate(#RequestBody JWTRequest jwtRequest)throws Exception{
try{
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(jwtRequest.getUsername(),jwtRequest.getPassword())
);
}catch (BadCredentialsException e){
throw new BadCredentialsException("INVALID_CREDENTIAL",e);
}
final UserDetails userDetails = jwtUserDetailsService.loadUserByUsername(jwtRequest.getUsername());
final String token = jwtUtil.generateToken(userDetails);
return new JWTResponse(token);
}
Everytime, I hit on authenticate endpoint somehow, service is being executed first with null value.
.security.authentication.BadCredentialsException: User not found with Username: NONE_PROVIDED
at com.fiserv.dc.service.JwtUserDetailsService.loadUserByUsername(JwtUserDetailsService.java:18) ~[main/:na]
I am relatively new to Spring Security and I am attempting to create an endpoint for logging out an user. The code I have tried so far:
public ResponseEntity<String> logout(HttpServletRequest request, HttpServletResponse response) {
// Authentication auth = SecurityContextHolder.getContext().getAuthentication();
// if (auth != null) {
// new SecurityContextLogoutHandler().logout(request, response, auth);
// System.out.println("logging out");
// return new ResponseEntity<>(HttpStatus.OK);
// }
try {
request.logout();
System.out.println("successful logout");
} catch (ServletException e) {
e.printStackTrace();
}
return new ResponseEntity<>(HttpStatus.OK);
}
My UserDetailsServiceImpl:
#Service
public class AccountDetailsServiceImpl implements UserDetailsService {
private final AccountRepository accountRepository;
public AccountDetailsServiceImpl(AccountRepository accountRepository) {
this.accountRepository = accountRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Account account = accountRepository.findByUsernameOrEmail(username, username);
if (account == null) {
throw new UsernameNotFoundException(username);
}
return new CustomUserDetails(account);
}
}
My security configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, securityConstraintsProperties.getSignUpUrl()).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager(), getApplicationContext(), securityConstraintsProperties))
.addFilter(new JWTAuthorizationFilter(authenticationManager(), securityConstraintsProperties))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
I have seen both possibilities in different Stackoverflow answers, but unfortunately none of them is working for me. When I perform a request after logging out, the request is still possible. How is that possible?
Thank you in advance!
I have following websocket security configuration:
#Configuration
public class WebSocketAuthorizationSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
#Override
protected void configureInbound(final MessageSecurityMetadataSourceRegistry messages) {
// You can customize your authorization mapping here.
//messages.anyMessage().authenticated();
messages.simpDestMatchers("/hello").hasRole("ADMIN");
}
// TODO: For test purpose (and simplicity) i disabled CSRF, but you should re-enable this and provide a CRSF endpoint.
#Override
protected boolean sameOriginDisabled() {
return true;
}
}
I expect that only admin can send messages into the /hello topic.
and following security configuration:
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String SECURE_ADMIN_PASSWORD = "rockandroll";
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.formLogin()
.loginPage("/index.html")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/sender.html")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/index.html")
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/js/**", "/lib/**", "/images/**", "/css/**", "/index.html", "/","/*.css","/webjars/**", "/*.js").permitAll()
.antMatchers("/websocket").hasRole("ADMIN")
.requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ADMIN")
.anyRequest().authenticated();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new AuthenticationProvider() {
#Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
List<GrantedAuthority> authorities = SECURE_ADMIN_PASSWORD.equals(token.getCredentials()) ?
AuthorityUtils.createAuthorityList("ROLE_ADMIN") : null;
return new UsernamePasswordAuthenticationToken(token.getName(), token.getCredentials(), authorities);
}
});
}
}
Also I have following websocket controller:
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public Greeting greeting(#Payload HelloMessage message, Principal principal) throws Exception {
Thread.sleep(1000); // simulated delay
simpMessagingTemplate.convertAndSendToUser(principal.getName(), "/queue/greetings", new Greeting("Ololo"));
return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
}
Next I login as user/pass(It is not admin).
And client successfully sends messages to the /hello topic:
stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()}));
and method greetinginvokes successfully.
What do I wrong?
It became working after I had provided following definition(added /app):
messages.simpDestMatchers("/app/hello").hasRole("ADMIN");
I am new to jwt and im using some resources from the net to help me get to understand jwt properly , I am able now to generate a token and access a resource that requires authorisation, first , i have a controller like
#RequestMapping("/token")
public class TokenController {
private JwtGenerator jwtGenerator;
public TokenController(JwtGenerator jwtGenerator) {
this.jwtGenerator = jwtGenerator;
}
#PostMapping
public String generate(#RequestBody final JwtUser jwtUser) {
return jwtGenerator.generate(jwtUser);
}
excuse me for i will be posting a lot of code.I am using postman for testing
so when i pass this as a post
{
"useNe" : "ter",
"paord":"123",
"role":"ain"
} or
{
"username" : "ter",
"is":"123",
"role":"admin"
}
I am generating a token ,it should require a username and password i think before a jwt token should be produced and i want to implement a proper login ,below is the security config and other classes that i have
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationProvider authenticationProvider;
#Autowired
private JwtAuthenticationEntryPoint entryPoint;
#Bean
public AuthenticationManager authenticationManagerBean() {
return new ProviderManager(Collections.singletonList(authenticationProvider));
}
#Bean
public JwtAuthenticationTokenFilter authenticationTokenFilter() throws Exception {
JwtAuthenticationTokenFilter filter = new JwtAuthenticationTokenFilter();
filter.setAuthenticationManager(authenticationManager());
// we set success handler so that we overide the default redirection
filter.setAuthenticationSuccessHandler(new JwtSuccessHandler());
return filter;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers("**/rest/**").authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(entryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(authenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
http.headers().cacheControl();
}
}
how can i create a login that will generate a token , or is that not the standard for jwt , also i want to have two types of role user and admin, admin can access all resources while user can access some , here are other classes
public class JwtAuthenticationTokenFilter extends AbstractAuthenticationProcessingFilter {
public JwtAuthenticationTokenFilter() {
super("/rest/**");
}
#Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse)
throws AuthenticationException, IOException, ServletException {
String header = httpServletRequest.getHeader("Authorisation");
if (header == null || !header.startsWith("Token")) {
throw new RuntimeException("JWT Token is missing");
}
String authenticationToken = header.substring(6);
JwtAuthenticationToken token = new JwtAuthenticationToken(authenticationToken);
return getAuthenticationManager().authenticate(token);
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
super.successfulAuthentication(request, response, chain, authResult);
chain.doFilter(request, response);
}
}
And my jwt generator should this not require a an username and password for login
#Component
public class JwtGenerator {
public String generate(JwtUser jwtUser) {
Claims claims = Jwts.claims()
.setSubject(jwtUser.getUserName());
claims.put("userId", String.valueOf(jwtUser.getId()));
claims.put("role", jwtUser.getRole());
return Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, "youtube")
.compact();
}
}
#Component
public class JwtAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
#Autowired
private JwtValidator validator;
#Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken)
throws AuthenticationException {
}
#Override
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken)
throws AuthenticationException {
JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) usernamePasswordAuthenticationToken;
String token = jwtAuthenticationToken.getToken();
JwtUser jwtUser = validator.validate(token);
if(jwtUser == null){
throw new RuntimeException("user token is incorrect" );
}
List<GrantedAuthority> grantedAuthorities = AuthorityUtils
.commaSeparatedStringToAuthorityList(jwtUser.getRole());
//we return an authenticated user
return new JwtUserDetails(jwtUser.getUserName(),jwtUser.getId(),token , grantedAuthorities);
}
#Override
public boolean supports(Class<?> aClass) {
return (JwtAuthenticationToken.class.isAssignableFrom(aClass));
}
}
How do i go about improving on this and end up with a proper login that generates a jwt and keeps it in headers for every request
I create Spring Boot + Spring Security app with JWT authentication and it works something like this:
client sends username, password to the login endpoint;
server checks if the provided credentials are valid and return a token;
client sends the token with every future request.
It's based on this example and approach for logout functionality which author suggest - just remove JWT token on client-side. I don't think that is good idea, and I'd like to implement it on back-end.
As I understand I need to remove JWT token of user which call logout REST method on server-side, after that when this user will call other REST methods he should get responses with 403 error. I investigated ways how can I add this feature and didn't find any appropriate solution.
Here are details of my implementation (there may be some differences from the example from the link above):
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private UserDetailsService userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.antMatchers(HttpMethod.POST, LOGIN_URL).permitAll()
.antMatchers(HttpMethod.GET, HEALTH_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
#Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
String user = Jwts.parser()
.setSigningKey(SECRET.getBytes())
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody()
.getSubject();
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
setFilterProcessesUrl(LOGIN_URL);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
User creds = new ObjectMapper().readValue(req.getInputStream(), User.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
new ArrayList<>())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) throws IOException, ServletException {
String token = Jwts.builder()
.setSubject(((org.springframework.security.core.userdetails.User) auth.getPrincipal()).getUsername())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET.getBytes())
.compact();
res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
}
}
public class SecurityConstants {
public static final String SECRET = "SecretKeyToGenJWTs";
public static final long EXPIRATION_TIME = 864_000_000; // 10 days
public static final String TOKEN_PREFIX = "Bearer ";
public static final String HEADER_STRING = "Authorization";
}