I have backend in spring, and front end in angular. I have problem to access a authorization header. In postman if i ping localhost:8080/login wiht correct data i get success authorization, but in my appliaction when i try to post with httpclient localhost:8080/login with correct data i get success response but without token in header, headers are be null.
Angular AuthService:
import { Injectable } from '#angular/core';
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpResponse } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class AuthService {
/** Data */
token!: string;
logged = false;
/** Constructor */
constructor(private http: HttpClient) { }
/** Authentiacate User */
login(username: string, password: string): void {
const credentials: LoginCredentials = {
username,
password
};
this.http.post('http://localhost:8080/login',
JSON.stringify(credentials), { observe: 'response' }).subscribe(res => {
// Not works... Authorization header is null object
console.log('Authorized success!' + res.headers.get('Authorization'));
this.logged = true;
},
(error: HttpErrorResponse) => {
console.log('Nie udana autoryzacja! KOD BLEDU: ' + error.status);
});
}
}
/** LoginCredentials */
interface LoginCredentials {
username: string;
password: string;
}
Spring AuthorizationFilter, SuccessHandler, SecurityConfig
package com.revo.ToDoList.handler;
import java.io.IOException;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.revo.ToDoList.config.SecurityConfig;
import com.revo.ToDoList.model.User;
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
/*
* On success authentication add token to header
*/
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
String token = JWT.create().withSubject(((User) authentication.getPrincipal()).getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + SecurityConfig.expirationTime)).sign(Algorithm.HMAC256(SecurityConfig.secret));
response.addHeader("Authorization", "Bearer " + token);
}
}
package com.revo.ToDoList.filter;
import java.io.BufferedReader;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.revo.ToDoList.model.LoginCredentials;
public class MyAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
/*
* Data
*/
private ObjectMapper objectMapper = new ObjectMapper();
/*
* Auth user
*/
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
try {
BufferedReader reader = request.getReader();
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
LoginCredentials authRequest = objectMapper.readValue(sb.toString(), LoginCredentials.class);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
authRequest.getUsername(), authRequest.getPassword()
);
setDetails(request, token);
return this.getAuthenticationManager().authenticate(token);
} catch (IOException e) {
throw new IllegalArgumentException(e.getMessage());
}
}
}
package com.revo.ToDoList.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import com.revo.ToDoList.filter.JwtAuthorizationFilter;
import com.revo.ToDoList.filter.MyAuthenticationFilter;
import com.revo.ToDoList.handler.MyAuthenticationSuccessHandler;
import com.revo.ToDoList.service.UserService;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
/*
* Data
*/
#Autowired
private UserService userService;
public static final String secret = "ACAB SKURWYSYNY";
public static final long expirationTime=86400000;
/*
* Http Security Rules
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/user/register").permitAll()
.anyRequest().authenticated().and()
.addFilterBefore(authFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new JwtAuthorizationFilter(super.authenticationManagerBean(), userService), BasicAuthenticationFilter.class)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.exceptionHandling()
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
http.cors().disable().csrf().disable();
}
/*
* Auth Manager Configuration
*/
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
}
/*
* ENCODER
*/
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/*
* My Authentication Filter
*/
private MyAuthenticationFilter authFilter() throws Exception {
MyAuthenticationFilter authFilter = new MyAuthenticationFilter();
authFilter.setAuthenticationSuccessHandler(new MyAuthenticationSuccessHandler());
authFilter.setAuthenticationManager(super.authenticationManager());
authFilter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login", "POST"));
return authFilter;
}
}
It looks like a CORS issue.
Probably you need to set cors.allowed.headers
<init-param>
<param-name>cors.allowed.headers</param-name>
<param-value>Origin, Accept, X-Requested-With, Content-Type, Content-Disposition, Access-Control-Request-Method, **Access-Control-Request-Headers**</param-value>
</init-param>
If is spring, it might be something like below:
#Override
protected void configure(HttpSecurity http) throws Exception {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedHeaders(List.of("Authorization", "Cache-Control", "Content-Type"));
........
.........
}
I got a similar problem once,
You may try adding below to your main class
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("http://localhost:8080");
}
};
}
Related
I followed a tutorial on Spring Security. I have set up an authentication that works: here is my authentication filter.
package com.securitycrud.crudTest.filter;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
#Slf4j
public class CustomAuthentificationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
public CustomAuthentificationFilter(AuthenticationManager authenticationManager){
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username = request.getParameter("username");
String password = request.getParameter("password");
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
return authenticationManager.authenticate(authenticationToken);
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
User user = (User)authentication.getPrincipal();
Algorithm algorithm = Algorithm.HMAC256("secret".getBytes());
String access_token = JWT.create()
.withSubject(user.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis()+ 10 * 60 * 1000))
.withIssuer(request.getRequestURL().toString())
.withClaim("roles", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining()))
.sign(algorithm);
String refresh_token = JWT.create()
.withSubject(user.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis()+ 30 * 60 * 1000))
.withIssuer(request.getRequestURL().toString())
.withClaim("roles", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining()))
.sign(algorithm);
Map<String, String> tokens = new HashMap<>();
tokens.put("access_token", access_token);
tokens.put("refresh_token", refresh_token);
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(), tokens);
}
}
Then here is the authorization and config class.
package com.securitycrud.crudTest.security;
import com.securitycrud.crudTest.filter.CustomAuthentificationFilter;
import com.securitycrud.crudTest.filter.CustomAuthorizationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import static org.springframework.http.HttpMethod.GET;
import static org.springframework.http.HttpMethod.POST;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;
#Configuration #EnableWebSecurity #RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
CustomAuthentificationFilter customAuthentificationFilter = new CustomAuthentificationFilter(authenticationManagerBean());
customAuthentificationFilter.setFilterProcessesUrl("/api/login");
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(STATELESS);
// en dessous: les url qu'on ne veut pas sécuriser par l'authentification: ex page d'acceuil
http.authorizeRequests().antMatchers("/api/login", "/api/token/refresh").permitAll();
http.authorizeRequests().antMatchers("/api/users").hasAnyRole("ROLE_ADMIN","ROLE_USER");
http.authorizeRequests().antMatchers(POST, "/api/user/save/**").hasAnyAuthority("ROLE_ADMIN");
http.authorizeRequests().anyRequest().authenticated();
http.addFilter(customAuthentificationFilter);
http.addFilterBefore(new CustomAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception{
return super.authenticationManagerBean();
}
}
package com.securitycrud.crudTest.filter;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import static java.util.Arrays.stream;
import static org.springframework.http.HttpHeaders.AUTHORIZATION;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
#Slf4j
public class CustomAuthorizationFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if(request.getServletPath().equals("/api/login")||request.getServletPath().equals("/api/token/refresh") ){
filterChain.doFilter(request,response);
} else {
String authorizationHeader = request.getHeader(AUTHORIZATION);
if(authorizationHeader != null && authorizationHeader.startsWith("Bearer ")){
try {
String token = authorizationHeader.substring("Bearer ".length());
Algorithm algorithm = Algorithm.HMAC256("secret".getBytes());
JWTVerifier jwtVerifier = JWT.require(algorithm).build();
DecodedJWT decodedJWT = jwtVerifier.verify(token);
String username = decodedJWT.getSubject();
String[] roles = decodedJWT.getClaim("roles").asArray(String.class);
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
stream(roles).forEach(role -> {
authorities.add(new SimpleGrantedAuthority(role));
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
try {
filterChain.doFilter(request, response);
} catch (IOException | ServletException e) {
throw new RuntimeException(e);
}
});
}catch (Exception exception){
log.error("error loggin in: {}", exception.getMessage());
response.setHeader("error", exception.getMessage());
response.setStatus(FORBIDDEN.value());
Map<String, String> error = new HashMap<>();
error.put("error_token", exception.getMessage());
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(), error);
}
} else {
filterChain.doFilter(request, response);
}
}
}
}
Nevertheless, when I try on Postman, I connect well, and I get the token well (and I am role user)
Then, I test the GET api/users request.
And there I can't. I get the following error:
403 Forbidden: {
"error_token": "The input is not a valid base 64 encoded string."
}
I don't understand.... I tried to search but no way...
Does anyone have an idea?
The title is self explanatory, I have this API made in Spring, and after JWT implementation I can login, but the gets doesn't work anymore.
But if I comment the the CorsConfigurationSource corsConfigurationSource() and the Lombok annotation #RequiredArgsConstructor in SecurityConfig, the login stop to work, but all the gets work beautifully.
I'll post the class SecurityConfig, AuthorizationFilter and the AuthenticationFilter respectively for you guys try to see whats going wrong.
import org.alterdata.shopback.app.security.AuthenticationFilter;
import org.alterdata.shopback.app.security.AuthorizationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
#Configuration
#EnableWebSecurity
#RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
UserDetailsService userDetailsService;
#Autowired
BCryptPasswordEncoder bCryptPasswordEncoder;
#Override
protected void configure(HttpSecurity http) throws Exception {
AuthenticationFilter authenticationFilter = new AuthenticationFilter(authenticationManagerBean());
http.cors().and().csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests().antMatchers("/login").permitAll()
.antMatchers("/cadastrar/**").hasAnyAuthority("ADMIN")
.antMatchers("/cadastro/**").hasAnyAuthority("ADMIN")
.anyRequest().authenticated();
http.addFilter(authenticationFilter);
http.addFilterBefore(new AuthorizationFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
#Override
public AuthenticationManager authenticationManager()throws Exception{
return super.authenticationManager();
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE","OPTIONS", "HEAD"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Arrays;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.auth0.jwt.exceptions.JWTVerificationException;
import io.jsonwebtoken.JwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.bytebuddy.implementation.bind.annotation.IgnoreForBinding.Verifier;
import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
#Slf4j
public class AuthorizationFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if(request.getServletPath().equals("/login")) {
filterChain.doFilter(request, response);
}else {
String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
if(authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
try {
String token = authorizationHeader.substring("Bearer ".length());
Algorithm algorithm = Algorithm.HMAC256("segredinho".getBytes());
JWTVerifier jwtVerifier = JWT.require(algorithm).build();
DecodedJWT decodedJWT = jwtVerifier.verify(token);
String user = decodedJWT.getSubject();
String [] roles = decodedJWT.getClaim("roles").asArray(String.class);
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
Arrays.stream(roles).forEach(role ->{
authorities.add(new SimpleGrantedAuthority(role));
});
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user,null, authorities);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
filterChain.doFilter(request, response);
}catch(Exception e){
log.error("erro ao realizar o login! {}", e.getMessage());
response.setHeader("erro", e.getMessage());
response.setStatus(401);
Map<String, String> error = new HashMap<>();
error.put("mensagem de erro", e.getMessage());
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(), error);
}
}else {
filterChain.doFilter(request, response);
}
}
}
}
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import org.springframework.web.bind.annotation.CrossOrigin;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
#CrossOrigin(origins = {"*"})
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
public AuthenticationFilter (AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
try{
String nome = request.getParameter("nome");
String senha = request.getParameter("senha");
UsernamePasswordAuthenticationToken uspsToken = new UsernamePasswordAuthenticationToken(nome, senha);
//response.setHeader("teste", String.valueOf(uspsToken));
return authenticationManager.authenticate(uspsToken);
}catch (Exception e){
throw new RuntimeException();
}
}
// #Override
// public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
// throws AuthenticationException {
// try{
// UserPasswordAuthRequest userPasswordAuthRequest = new ObjectMapper()
// .readValue(request.getInputStream(), UserPasswordAuthRequest.class);
//
// Authentication authentication = new UsernamePasswordAuthenticationToken(
// userPasswordAuthRequest.getNome(),
// userPasswordAuthRequest.getSenha()
// );
// return authenticationManager.authenticate(authentication);
// }catch (IOException e){
// throw new RuntimeException();
// }
// }
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
User user = (User) authResult.getPrincipal();
Algorithm algorithm = Algorithm.HMAC256("segredinho".getBytes());
String tokenAcesso = JWT.create().withSubject(user.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + 10*60*1000*60))
.withIssuer(request.getRequestURL()
.toString()).withClaim("roles", user.getAuthorities()
.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()))
.sign(algorithm);
String tokenRefresh = JWT.create().withSubject(user.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + 10*60*1000*60))
.withIssuer(request.getRequestURL()
.toString()).withClaim("roles", user.getAuthorities()
.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()))
.sign(algorithm);
Map<String, String> tokens = new HashMap<>();
tokens.put("tokenacesso", tokenAcesso);
tokens.put("tokenrefresh", tokenRefresh);
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(),tokens);
}
}
First try to call your api with Postman, else i think you need to add authorization jwt header to every api request after you login.
export default function authHeader() {
const user = JSON.parse(localStorage.getItem('user'));
if (user && user.accessToken) {
return { Authorization: 'Bearer ' + user.accessToken };
} else {
return {};
}
}
and then call your api like this:
return axios.get(API_URL + 'user', { headers: authHeader() });
for more informations try to visit: https://www.bezkoder.com/react-jwt-auth/
I just fixed using this global cors
#Bean
public CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
I have implemented below code for spring security.
I am using spring version 2.3.0.BUILD-SNAPSHOT with security oauth-2 version 2.3.0.RELEASE
Is It possible to remove / revoke granted access token once its expiry time is completed? If so which class/method should be place and where, to achieve the goal ?
AuthenticationService.java
package com.oauth.config;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.oauth.model.UserInfo;
import com.oauth.service.UserService;
#Service("authenticationService")
public class AuthenticationService implements UserDetailsService {
#Autowired
private UserService userService;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
HttpServletRequest request = (
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
System.out.println("userId: " + request.getParameter("userId"));
UserInfo user = null;
try {
user = userService.getUserInfoByUserName(username);
GrantedAuthority authority = new SimpleGrantedAuthority(user.getRole());
return new User(user.getUserName(), user.getPassword(), Arrays.asList(authority));
} catch (Exception e) {
e.printStackTrace();
throw new UsernameNotFoundException("Invalid user id or password.");
}
}
}
Outh2Configuration.java
package com.oauth.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
#Configuration
public class OAuth2Configuration {
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
/*
* #Autowired private CustomLogoutSuccessHandler customLogoutSuccessHandler;
*/
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
//.and().logout().logoutUrl("/oauth/logout").logoutSuccessHandler(customLogoutSuccessHandler)
.and().authorizeRequests()
.antMatchers("/noAuth/**","/customerNoAuth/**" ,"/oauth/token").permitAll()
.anyRequest().denyAll()
.and().csrf().disable()
.exceptionHandling();
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter implements EnvironmentAware {
private static final String PROP_CLIENTID = "client";
private static final String PROP_SECRET = "secret";
private static final int PROP_TOKEN_VALIDITY_SECONDS = 60 * 60 * 24 * 7;
#Autowired
private DataSource dataSource;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Autowired
#Qualifier("myAuthenticationManager")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
//.tokenStore(tokenStore())
.authenticationManager(authenticationManager)
.tokenServices(tokenServices());
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
String secretEncoded = passwordEncoder().encode(PROP_SECRET);
clients
.inMemory()
.withClient(PROP_CLIENTID)
.scopes("read", "write", "trust")
.authorities("ADMIN", "USER")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.secret(secretEncoded)
.accessTokenValiditySeconds(PROP_TOKEN_VALIDITY_SECONDS)
.refreshTokenValiditySeconds(PROP_TOKEN_VALIDITY_SECONDS);
}
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void setEnvironment(Environment arg0) {
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
CustomTokenService defaultTokenServices = new CustomTokenService();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
}
}
WebSecurityConfiguration.java
package com.oauth.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationService authenticateService;
#Bean(name="myAuthenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/noAuth/**",
"/customerNoAuth/**",
"/state/**",
"/country/**",
"/violation/**",
"/ticketViolation/**",
"/api/zipcode/**");
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(authenticateService)
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
CustomTokenService.java
package com.oauth.config;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.transaction.annotation.Transactional;
import lombok.Synchronized;
public class CustomTokenService extends DefaultTokenServices {
private final Logger log = LogManager.getFormatterLogger(getClass());
#Override
#Transactional
#Synchronized
public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
try {
int count = 0;
OAuth2AccessToken accessToken = null;
try{
accessToken = super.createAccessToken(authentication);
}catch(DuplicateKeyException e){
do{
log.info("===========DuplicateKeyException===========");
log.info(String.format("Duplicate user found for %s",authentication.getUserAuthentication().getPrincipal()));
Thread.sleep(500);
try {
return accessToken = super.createAccessToken(authentication);
} catch (DuplicateKeyException e2) {
++count;
log.error("Attempt count " + count);
}
}while(count<=5);
}
return accessToken;
}catch (Exception ex) {
log.info(String.format("Exception while creating access token %s",ex));
}
return null;
}
}
CustomLogoutSuccessHandler.java
package com.oauth.config;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.authentication.AbstractAuthenticationTargetUrlRequestHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
/**
* Spring Security logout handler
*/
//#Component
public class CustomLogoutSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler implements LogoutSuccessHandler {
private static final String BEARER_AUTHENTICATION = "bearer ";
private static final String HEADER_AUTHORIZATION = "authorization";
#Autowired
private TokenStore tokenStore;
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
String token = request.getHeader(HEADER_AUTHORIZATION);
if (token != null && token.startsWith(BEARER_AUTHENTICATION)) {
String accessToken = token.substring(token.indexOf(" ") + 1);
OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(accessToken);
if (oAuth2AccessToken != null) {
tokenStore.removeAccessToken(oAuth2AccessToken);
}
}
response.setStatus(HttpServletResponse.SC_OK);
}
}
You should not store tokens in ur DB.If anyone can access ur db ,he can easily get access to every data.Again if u want to store then i think u can make a scheduler to delete token.
How to handle UsernameNotFoundException ?
In spring security when username not found the UserDetailsService implementation throws a UsernameNotFoundException. For example like this:
#Override
#Transactional
public UserDetails loadUserByUsername(java.lang.String username) throws UsernameNotFoundException {
logger.info("Load user by username: {}", username);
User user = userRepository.findUserByUsername(username).orElseThrow(
() -> new UsernameNotFoundException("User Not Found with -> username or email: " + username));
return UserPrinciple.build(user);
}
I would like to build a custom "User not found REST response".
How should I catch/handle this exception? I have implemented a handler method in the WebSecurityConfigurerAdapter implementation the handler:
private static void handleException(HttpServletRequest req, HttpServletResponse rsp, AuthenticationException e)
throws IOException {
PrintWriter writer = rsp.getWriter();
writer.println(new ObjectMapper().writeValueAsString(new AuthResponse("", null, null, null, null,
"Authentication failed.", false)));
rsp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
But this method should wait for an AuthenticationException exception which, and during runtime the type of the exception is java.lang.NullPointerException so I'm not able to cast or retrieve the the initial UsernameNotFoundException.
Any advice would be appreciated.
Many many regards :).
Security layer comes before anything in the controllers and #ControllerAdvice.
Hence #ControllerAdvice isn't an option since UsernameNotFoundException which is a subclass of AuthenticationException is thrown during authenticaton, making your exception handlers in #ControllerAdvice unreachable.
You can only use #ControllerAdvice and ResponseEntityExceptionHandler if you are throwing UsernameNotFoundException inside controller or any others beans referenced from the controllers.
Here is my suggestion - that you implement AuthenticationFailureHandler and use it with AuthenticationFilter that you are using for your security configuration.
Spring boot security comes with about 4 handler interfaces for security related issues
AccessDeniedHandler - this handles issues like when a user not having required roles.
AuthenticationEntryPoint - this handles issues like when a user tries to access a resource without appropriate authentication elements.
AuthenticationFailureHandler - this handles issues like when a user is not found(i.e. UsernameNotFoundException) or other exceptions thrown inside authentication provider. In fact, this handles other authentication exceptions that are not handled by AccessDeniedException and AuthenticationEntryPoint.
AuthenticationSuccessHandler - this helps to do stuff like redirection after a user is successfully authenticated.
See the following example snippets for the implementation of all the 4 interfaces. Please customize these to your taste.
AccessDeniedHandler implementation
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
#Component
public class RestAccessDeniedHandler implements AccessDeniedHandler {
#Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
Map<String,Object> response = new HashMap<>();
response.put("status","34");
response.put("message","unauthorized api access");
//httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
OutputStream out = httpServletResponse.getOutputStream();
ObjectMapper mapper = new ObjectMapper();
mapper.writerWithDefaultPrettyPrinter().writeValue(out,response);
//mapper.writeValue(out, response);
out.flush();
}
}
AuthenticationEntryPoint Implementation
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
#Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
Map<String,Object> response = new HashMap<>();
response.put("status","34");
response.put("message","unauthorized access");
httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
OutputStream out = httpServletResponse.getOutputStream();
ObjectMapper mapper = new ObjectMapper();
mapper.writerWithDefaultPrettyPrinter().writeValue(out, response);
out.flush();
}
}
AuthenticationFailureHandler implementation
package com.ibiller.webservices.security;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
#Component
public class RestAuthenticationFailureHandler implements AuthenticationFailureHandler
{
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse httpServletResponse,
AuthenticationException ex) throws IOException, ServletException
{
Map<String,Object> response = new HashMap<>();
response.put("status","34");
response.put("message","unauthorized access");
httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
OutputStream out = httpServletResponse.getOutputStream();
ObjectMapper mapper = new ObjectMapper();
mapper.writerWithDefaultPrettyPrinter().writeValue(out, response);
out.flush();
}
}
AuthenticationSuccessHandler implementation
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
#Component
public class RestSuccessHandler implements AuthenticationSuccessHandler {
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
Set<String> roles =
AuthorityUtils.authorityListToSet(authentication.getAuthorities());
if (roles.contains("ROLE_ADMIN")) {
//do something
}
}
}
This is the Security configuration that extends WebSecurityConfigurerAdapter that connects everything together.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final RequestMatcher PROTECTED_URLS = new OrRequestMatcher(
new AntPathRequestMatcher("/v1/**"),new AntPathRequestMatcher("/admin/**")
);
AuthenticationProvider provider;
public SecurityConfiguration(final AuthenticationProvider authenticationProvider) {
super();
this.provider=authenticationProvider;
}
#Override
protected void configure(final AuthenticationManagerBuilder auth) {
auth.authenticationProvider(provider);
}
#Override
public void configure(final WebSecurity webSecurity) {
webSecurity.ignoring().antMatchers("/info/**");//url that will be ignored
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
.authenticationEntryPoint(authenticationEntryPoint())
.and()
.authenticationProvider(provider)
.addFilterBefore(authenticationFilter(), AnonymousAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/v1/**").hasRole("API")
.antMatchers("/admin/**").hasAnyRole("SUPER_ADMIN","ADMIN")
.and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.logout().disable();
}
#Bean
AuthenticationFilter authenticationFilter() throws Exception {
final AuthenticationFilter filter = new AuthenticationFilter(PROTECTED_URLS);
filter.setAuthenticationManager(authenticationManager());
filter.setAuthenticationSuccessHandler(successHandler());
filter.setAuthenticationFailureHandler(authenticationFailureHandler());
return filter;
}
#Bean
RestAccessDeniedHandler accessDeniedHandler() {
return new RestAccessDeniedHandler();
}
#Bean
RestAuthenticationEntryPoint authenticationEntryPoint() {
return new RestAuthenticationEntryPoint();
}
#Bean
RestAuthenticationFailureHandler authenticationFailureHandler(){
return new RestAuthenticationFailureHandler();
}
#Bean
RestSuccessHandler successHandler(){
return new RestSuccessHandler();
}
}
I don't know the structure of your project, but a usual solution in this case is using #ControllerAdvice mechanism (separated class or in controller):
#ControllerAdvice
public class CustomExceptionHandler {
#ExceptionHandler(value = UsernameNotFoundException.class)
public ResponseEntity handle(final UsernameNotFoundException exception) {
...//set headers, response attributes and response body
}
}
In the class that inherits from UsernamePasswordAuthenticationFilter you have to override the method unsuccessfulAuthentication
It calls the super class method, but what it does it's it redirects to another context of error, which makes the Authorization filter activates.
Instead, just fill the request information as your client is expecting (Json in my case)
#Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse res, AuthenticationException failed) throws IOException, ServletException {
res.addHeader("Access-Control-Allow-Origin", "*");
res.setStatus(HttpServletResponse.SC_OK);
ObjectMapper mapper = new ObjectMapper();
ObjectNode message = mapper.createObjectNode();
message.put("success", false);
message.put("message", "Invalid credentials");
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(message);
PrintWriter out = res.getWriter();
res.setContentType("application/json");
res.setCharacterEncoding("UTF-8");
out.print(json);
out.flush();
}
I am building a project with Spring Security and Spring Boot, but when I login with ajax request, Spring Security's request matcher can not match the login request /auth/login,
I want to know how I could configure it correctly.
This is my WebSecurity Configuration:
package com.keanu.blog.web.config;
import com.keanu.blog.web.security.GlobalAuthenticationFilter;
import com.keanu.blog.web.security.LoginAuthenticationProvider;
import com.keanu.blog.web.security.JwtAuthenticationFilter;
import com.keanu.blog.web.security.TokenAuthenticationProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
/**
*
* #author leiyongqi
* #date 2019/07/27
*/
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AccessDeniedHandler accessDeniedHandler;
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/auth/**", "/registry").permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/auth/login")
.defaultSuccessUrl("/")
.failureUrl("/login?error=true")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/login")
.and()
.addFilter(globalAuthenticationFilter());
}
#Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
return new JwtAuthenticationFilter(authenticationManager());
}
#Bean("loginAuthenticationProvider")
public AuthenticationProvider loginAuthenticationProvider() {
return new LoginAuthenticationProvider();
}
#Bean("tokenAuthenticationProvider")
public AuthenticationProvider tokenAuthenticationProvider() {
return new TokenAuthenticationProvider();
}
#Bean
public GlobalAuthenticationFilter globalAuthenticationFilter() throws Exception {
GlobalAuthenticationFilter globalAuthenticationFilter = new GlobalAuthenticationFilter();
globalAuthenticationFilter.setAuthenticationManager(authenticationManager());
return globalAuthenticationFilter;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(loginAuthenticationProvider())
.authenticationProvider(tokenAuthenticationProvider())
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/assets/**","/druid/**");
}
#Bean
public AccessDeniedHandler accessDeniedHandler() {
return new CustomAccessDeniedHandler();
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
And this is My GlobalAuthenticationFilter.class, every request can be intercepted by this filter.
package com.keanu.blog.web.security;
import com.keanu.blog.common.constant.AuthConstant;
import com.keanu.blog.common.utils.AesUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Custom Authentication Filter (Both Username and Token)
* #author leiyongqi
* #date 2019/08/01
*/
#Slf4j
public class GlobalAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private String keyParameter = "key";
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try {
// Authenticate with username
if (isLoginRequest(request, response)) {
Authentication authentication = processLogin(request, response);
successfulAuthentication(request, response, chain, authentication);
return;
}
// If the request isn't login request, authenticate with jwt token
// TODO Authenticate with jwt token
String token = obtainJwtToken(request);
} catch (AuthenticationException e) {
unsuccessfulAuthentication(request, response, e);
return;
}
chain.doFilter(request, response);
}
private String obtainJwtToken(HttpServletRequest request) {
return request.getHeader(AuthConstant.JWT_TOKEN_HEADER);
}
private boolean isLoginRequest(HttpServletRequest request, HttpServletResponse response) {
return requiresAuthentication(request, response) && "POST".equalsIgnoreCase(request.getMethod());
}
private String obtainSecretKey(HttpServletRequest request) {
return request.getParameter(keyParameter);
}
private Authentication processLogin(HttpServletRequest request, HttpServletResponse response) {
String username = obtainUsername(request);
String encryptPassword = obtainPassword(request);
String key = obtainSecretKey(request);
if (log.isDebugEnabled()) {
log.debug("处理登录请求:username[{}], password[{}], secretKey[{}]", username, encryptPassword, key);
}
// 解密密码以及用 BCryptPasswordEncoder 加密
String password = null;
try {
password = AesUtil.encryptPassword(AesUtil.decryptAes(encryptPassword, key));
} catch (Exception e) {
log.error("解密密码失败:password[{}], key[{}], error[{}]", encryptPassword, key, e.getMessage());
}
Authentication authentication = new UsernamePasswordAuthenticationToken(username, password);
Authentication responseAuth = getAuthenticationManager().authenticate(authentication);
if (responseAuth == null || !responseAuth.isAuthenticated()) {
throw new InternalAuthenticationServiceException("Unable to authenticate User for provided credentials");
}
if (log.isDebugEnabled()) {
log.debug("登录用户 [{}] 验证成功", username);
}
return responseAuth;
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
response.setStatus(HttpStatus.OK.value());
response.getWriter().write("");
}
}
but if I use ajax to send login request using url /auth/login, Spring Security can not match the login request so I can't handle the login request.
this is my Login Page's ajax code:
$.ajax({
type: "POST",
url: loginUrl, // loginUrl => '/auth/login'
contentType: "application/json;charset=utf-8",
data: JSON.stringify({'username': username, 'password': encryptPwd, 'secretKey': key}),
dataType: "json",
async: true,
success: function (data) {
window.localStorage.setItem("JWT-TOKEN", data);
window.location.href = "/";
},
error: function (e) {
alert("登录失败:" + e.message);
}
})
Through checking the source code, the request can't go into the 'handle login' block because requiresAuthentication(request) can't match the login pattern with '/auth/login', but I don' t know why.
I expected the login request with ajax to be intercepted by Spring Security so the request could go into 'handle login' block I showed above.