I'm writing test for a Rest API controller. This endpoint is accessible without any authorization:
#EnableWebSecurity
#Configuration
#Import(AppConfig.class)
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsRepository accountRepository;
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Autowired
private JWTAuthenticationFilter jwtAuthenticationFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.anyRequest().authenticated().and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
/*
* Apparently, permitAll() doesn't work for custom filters, therefore we ignore the signup and login endpoints
* here
*/
#Override
public void configure(WebSecurity web)
throws Exception {
web.ignoring()
.antMatchers(HttpMethod.POST, "/login")
.antMatchers(HttpMethod.POST, "/signup");
}
/*
* set user details services and password encoder
*/
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsServiceBean()).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/* Stopping spring from adding filter by default */
#Bean
public FilterRegistrationBean rolesAuthenticationFilterRegistrationDisable(JWTAuthenticationFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
}
The JWTAuthenticationFilter class:
#Component
public class JWTAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
#Autowired
private UserDetailsService customUserDetailsService;
private static Logger logger = LoggerFactory.getLogger(JWTAuthenticationFilter.class);
private final static UrlPathHelper urlPathHelper = new UrlPathHelper();
final static String defaultFilterProcessesUrl = "/**";
public JWTAuthenticationFilter() {
super(defaultFilterProcessesUrl);
super.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(defaultFilterProcessesUrl)); //Authentication will only be initiated for the request url matching this pattern
setAuthenticationManager(new NoOpAuthenticationManager());
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
Authentication authentication = AuthenticationService.getAuthentication(request, customUserDetailsService);
return getAuthenticationManager().authenticate(authentication);
}
#Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
logger.debug("failed authentication while attempting to access "+ urlPathHelper.getPathWithinApplication((HttpServletRequest) request));
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,"Authentication Failed");
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(authResult);
chain.doFilter(request, response);
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
super.doFilter(req, res, chain);
}
}
When I make a request (using postman) to 'signup' endpoint it works fine. But when I run the test, it hits doFilter and fails, as it doesn't get authenticated.
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class AuthenticationControllerFTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private AuthenticationManager authenticationManager;
#Test
public void testCreate() throws Exception {
Authentication authentication = Mockito.mock(Authentication.class);
Mockito.when(authentication.getName()).thenReturn("DUMMY_USERNAME");
Mockito.when(
authenticationManager.authenticate(Mockito
.any(UsernamePasswordAuthenticationToken.class)))
.thenReturn(authentication);
String exampleUserInfo = "{\"name\":\"Test1234\",\"username\":\"test#test.com\",\"password\":\"Salam12345\"}";
RequestBuilder requestBuilder = MockMvcRequestBuilders
.post("/signup")
.accept(MediaType.APPLICATION_JSON).content(exampleUserInfo)
.contentType(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response = result.getResponse();
int status = response.getStatus();
String content = response.getContentAsString();
System.out.println(content);
Assert.assertEquals("http response status is wrong", 200, status);
}
}
Any idea on how to fix this issue ?
The issue was resolved by adding the following code to the test class:
#Autowired
private WebApplicationContext context;
#Autowired
private Filter springSecurityFilterChain;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.addFilters(springSecurityFilterChain).build();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/**").permitAll()
.anyRequest().authenticated();
}
Related
Getting 401 for "/register" endpoint even after defining it as permitAll in antMatcher.
I am new to spring security.
I have configured two endpoints "/login" and "/register". I want "/register" to be available public and there should be a username/password check on accessing "/login".
My server.servlet.context-path is set to "/todo/user".
I tried to search for the solution but nothing would explain why i am facing the issue or may be i wasnt able to comprehend it being new to this.
Controller class
#Controller
#RequestMapping(value = "/register")
public class RegistrationController {
#Autowired
UserService userService;
#RequestMapping(value = "", method = RequestMethod.POST,
produces = "application/json")
#ResponseBody
public ResponseEntity<String> registration(#RequestBody #Valid UserRegistration userRegistration) {
Optional<UserRegistration> user = Optional.ofNullable(userRegistration);
if(!user.isPresent()) {
throw new UserDataNotAvailableException("User data is not available");
}
//TO-DO Write implementation of isUserValid
Optional<Boolean> isRegistrationSuccess = user.map(userService::registerNewUser);
return new ResponseEntity<String>("This is temp", HttpStatus.OK);
}
}
SecurityConfig
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.POST, "/register").permitAll()
.anyRequest().authenticated()
.and().addFilter(new
JwtAuthenticationFilter(authenticationManager()));
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
}
Authentication Filter
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private static final int EXPIRATION_TIME = 3900;
private static final String HEADER_NAME = "auth";
private AuthenticationManager authenticationManager;
#Value("${jwt.secret}")
private String secret;
public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
try{
UserLogin user = new ObjectMapper().readValue(request.getInputStream(), UserLogin.class);
return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getEmailId(), user.getPassword(),
new ArrayList<>()));
}catch (IOException e) {
throw new InternalServerException("There was a problem in processing the request");
}
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
FilterChain chain, Authentication authResult) throws IOException, ServletException {
String jwt = Jwts.builder().setSubject(((UserLogin)authResult.getPrincipal()).getEmailId())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.ES256, secret)
.compact();
response.addHeader(HEADER_NAME, jwt);
}
}
AS evident from security config class i have permitted all to access my "/register" endpoint.
But when i make a request on "/register" i get a 401.
This should not be the case and i should be able to access this particular endpoint without any JWT
I want to execute code on successful authentication to add certain roles to my user object:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${ad.domain}")
private String AD_DOMAIN;
#Value("${ad.url}")
private String AD_URL;
#Autowired
UserRoleComponent userRoleComponent;
private final Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class);
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
#Override
protected void configure(HttpSecurity http) throws Exception {
this.logger.info("Verify logging level"); //works
http
.authorizeRequests()
.anyRequest()
.fullyAuthenticated()
.and()
.formLogin()
.successHandler(new AuthenticationSuccessHandler() {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
userRoleComponent.testIt();
redirectStrategy.sendRedirect(request, response, "/");
}
})
.and()
.httpBasic();
http.formLogin().defaultSuccessUrl("/", true);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(AD_DOMAIN,
AD_URL);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
}
So I tried to add a new AuthenticationSuccessHandler to insert code to be executed in a component (this is where the roles should be added then).
In my UserRoleController:
#Component
public class UserRoleComponent {
private Logger logger = LoggerFactory.getLogger(UserRoleComponent.class);
public void testIt() {
this.logger.info("=============== User Authentication ==============");
System.out.println("================ User Authentication ================");
}
}
Now sadly when I login successfully there is no output printed. How can I solve this issue?
Edit: I verified the loggin level is correct and the system.out.println does not work also.
I have rest api application in Spring Boot 1.5.3, I'm using security to login and authenticate every request by token to my api. I want add my custom exception with unauthorized exception when user not found by token. Class with exception is added but every response has 500 code but I want 401 response code. Belowe is my code.
StatelessAuthenticationFilter
public class StatelessAuthenticationFilter extends GenericFilterBean {
private final TokenAuthenticationService tokenAuthenticationService;
public StatelessAuthenticationFilter(TokenAuthenticationService taService) {
this.tokenAuthenticationService = taService;
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(tokenAuthenticationService.getAuthentication((HttpServletRequest) req));
chain.doFilter(req, res);
}
StatelessLoginFilter
public class StatelessLoginFilter extends AbstractAuthenticationProcessingFilter {
private final TokenAuthenticationService tokenAuthenticationService;
private final UserServiceImpl userService;
public StatelessLoginFilter(String urlMapping, TokenAuthenticationService tokenAuthenticationService,
UserServiceImpl userDetailsService, AuthenticationManager authManager) {
super(new AntPathRequestMatcher(urlMapping));
this.userService = userDetailsService;
this.tokenAuthenticationService = tokenAuthenticationService;
setAuthenticationManager(authManager);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
String headerCredentials = request.getHeader("BasicAuth");
if (headerCredentials == null) {
throw new BadCredentialsException("No header in request");
}
String credentials = new String(Base64.decodeBase64(headerCredentials), "UTF-8");
if (!credentials.contains((":"))) {
throw new BadCredentialsException("Wrong header");
}
String [] credentialsArray = credentials.split(":");
String login = credentialsArray[0];
String password = credentialsArray[1];
final UsernamePasswordAuthenticationToken loginToken = new UsernamePasswordAuthenticationToken(login, password);
return getAuthenticationManager().authenticate(loginToken);
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
FilterChain chain, Authentication authentication) throws IOException, ServletException {
// Lookup the complete User2 object from the database and create an Authentication for it
final User authenticatedUser = userService.loadUserByUsername(authentication.getName());
final UserAuthentication userAuthentication = new UserAuthentication(authenticatedUser);
// Add the custom token as HTTP header to the response
tokenAuthenticationService.addAuthentication(response, userAuthentication);
// Add the authentication to the Security context
SecurityContextHolder.getContext().setAuthentication(userAuthentication);
}
MyOwnException
public class MyOwnException extends RuntimeException {
public MyOwnException(String message) {
super(message);
}
RestResponseEntityExceptionHandler
#ControllerAdvice
public class RestResponseEntityExceptionHandler extends DefaultHandlerExceptionResolver {
#ExceptionHandler(MyOwnException.class)
void handleMyOwnException(HttpServletResponse response) throws IOException {
response.sendError(HttpStatus.UNAUTHORIZED.value());
}
}
StatelessAuthenticationSecurityConfig
#EnableWebSecurity
#Configuration
#Order(1)
public class StatelessAuthenticationSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserServiceImpl userService;
#Autowired
private TokenAuthenticationService tokenAuthenticationService;
public StatelessAuthenticationSecurityConfig() {
super(true);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/login").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().hasRole("USER")
.anyRequest().hasRole("ADMIN").and()
// custom JSON based authentication by POST of {"username":"<name>","password":"<password>"} which sets the token header upon authentication
.addFilterBefore(new StatelessLoginFilter("/login", tokenAuthenticationService, userService, authenticationManager()), UsernamePasswordAuthenticationFilter.class)
// custom Token based authentication based on the header previously given to the client
.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService), UsernamePasswordAuthenticationFilter.class);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedOrigin("*");
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "DELETE", "OPTIONS"));
configuration.setExposedHeaders(Arrays.asList("x-auth-token"));
configuration.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Override
protected UserServiceImpl userDetailsService() {
return userService;
}
VoteApp
#SpringBootApplication
public class VoteApp {
public static void main(String[] args) {
SpringApplication.run(VoteApp.class, args);
}
#Bean
public Filter characterEncodingFilter() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
return characterEncodingFilter;
}
}
UserServiceImpl
#Service
public class UserServiceImpl implements org.springframework.security.core.userdetails.UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public final User loadUserByUsername(String username) throws UsernameNotFoundException {
final User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("user not found");
}
return user;
}
public User findByToken(String token) throws MyOwnException {
final User user = userRepository.findByToken(token);
if (user == null) {
throw new MyOwnException("user by token not found");
}
return user;
}
public void save(User user) {
userRepository.save(user);
}
}
Obviously #ControllerAdvice can't handle your exception because controller methods has not been called yet. I mean you exception being thrown in servlet filter. I think you going to have to catch it manually, smth like this:
public class StatelessAuthenticationFilter extends GenericFilterBean {
private final TokenAuthenticationService tokenAuthenticationService;
public StatelessAuthenticationFilter(TokenAuthenticationService taService) {
this.tokenAuthenticationService = taService;
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
Authentication auth = null;
try {
auth = tokenAuthenticationService.getAuthentication((HttpServletRequest) req);
} catch (MyOwnException e) {
SecurityContextHolder.clearContext();
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
return;
}
SecurityContextHolder.getContext().setAuthentication(auth);
chain.doFilter(req, res);
}
Add #ResponseStatus annotation to your exception handler of controller advice.
For more information visit - Exception Handling in Spring MVC
I have a org.springframework.web.filter.GenericFilterBean filter. I would like to throw an exception when user is not authorized and catch this exception with #ControllerAdvice. But it seems that the handler doesn't do that.
Filter method
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
ClientAuthentication auth = (ClientAuthentication) authenticationService.getAuthentication(httpRequest, httpResponse);
SecurityContextHolder.getContext().setAuthentication(auth);
if (auth.isAuthenticated()) {
chain.doFilter(request, response);
} else {
ObjectMapper mapper = new ObjectMapper();
response.setCharacterEncoding("UTF-8");
httpResponse.getWriter().write(mapper.writeValueAsString(auth.getInfo()));
}
}
It works but a disadvantage is that I want to catch exception and render exception message back to the client with respect to Content-Type and Accept HTTP header. This solution renders what I want but into JSON only and my application has to handle XML and JSON.
Exception handler
it works when I throw exceptions from #Controller or #RestController but not from HTTP filter.
#ControllerAdvice
class GlobalExceptionHandler {
private static final Logger LOGGER = LogManager.getLogger(GlobalExceptionHandler.class);
#ExceptionHandler(BadRequestException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public
#ResponseBody
ResponseMessage badRequestException(BadRequestException ex) {
return ex.getResponseMessage();
}
/** rest omitted .... **/
}
Update #1
This is my Spring Security config where AuthFilter filter is set.
#EnableWebSecurity
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Configuration
#Order(1)
public static class ApiSecurity extends WebSecurityConfigurerAdapter {
private static Logger LOG = LogManager.getLogger(ApiSecurity.class);
#Bean
AuthenticationEntryPoint authenticationEntryPoint() {
LOG.debug("authenticationEntryPoint bean");
return new RestAuthenticationEntryPoint();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
.and()
.servletApi()
.and()
.headers().cacheControl();
http.antMatcher(ApiController.API_ROOT + "/**").authorizeRequests().anyRequest().authenticated()
.and()
.addFilterBefore(new AuthFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(ApiController.API_ROOT + "/sandbox/**");
}
}
#Configuration
public static class WebFormSecurity extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService defaultUserService;
#Autowired
private PasswordEncoder passwordEncoder;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(defaultUserService).passwordEncoder(passwordEncoder);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/public/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().fullyAuthenticated().and().formLogin()
.loginPage("/login").failureUrl("/login?error").permitAll().and()
.logout().permitAll();
}
}
}
Here's my main Application config
#SpringBootApplication
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class)
.banner((environment, aClass, printStream) ->
System.out.println(stringBanner()))
.run();
}
}
And here's my spring security application config.
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private WebServiceAuthenticationEntryPoint unauthorizedHandler;
#Autowired
private TokenProcessingFilter authTokenProcessingFilter;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // Restful hence stateless
.and()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler) // Notice the entry point
.and()
.addFilter(authTokenProcessingFilter) // Notice the filter
.authorizeRequests()
.antMatchers("/resources/**", "/api/auth")
.permitAll()
.antMatchers("/greeting")
.hasRole("USER");
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user")
.password("password")
.roles("USER");
}
}
Here's my TokenProcessingFilter that extends UsernamePasswordAuthenticationFilter for my custom authentication filter
#Component
public class TokenProcessingFilter extends UsernamePasswordAuthenticationFilter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = this.getAsHttpRequest(request);
String authToken = this.extractAuthTokenFromRequest(httpRequest);
String userName = TokenUtils.getUserNameFromToken(authToken);
if (userName != null) {/*
UserDetails userDetails = userDetailsService.loadUserByUsername(userName);*/
UserDetails userDetails = fakeUserDetails();
if (TokenUtils.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
SecurityContextHolder.getContext().setAuthentication(authentication);
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
}
chain.doFilter(request, response);
}
private HttpServletRequest getAsHttpRequest(ServletRequest request){
if (!(request instanceof HttpServletRequest)) {
throw new RuntimeException("Expecting an HTTP request");
}
return (HttpServletRequest) request;
}
private String extractAuthTokenFromRequest(HttpServletRequest httpRequest) {
/* Get token from header */
String authToken = httpRequest.getHeader("x-auth-token");
/* If token not found get it from request parameter */
if (authToken == null) {
authToken = httpRequest.getParameter("token");
}
return authToken;
}
private UserDetails fakeUserDetails(){
UsernamePasswordAuthenticationToken authenticationToken = new
UsernamePasswordAuthenticationToken("user","password");
List<SimpleGrantedAuthority> auth= new ArrayList<>();
auth.add(new SimpleGrantedAuthority("USER"));
return new User("user","password",auth);
}
}
however when running the application, I encounter this exception message. What am I missing?
An exception occured while running. null: InvocationTargetException:
Unable to start embedded container; nested exception is
org.springframework.boot.context.embedded.EmbeddedServletContainerException:
Unable to start embedded Tomcat: Error creating bean with name
'tokenProcessingFilter' defined in file
[C:\Users\kyel\projects\app\target\classes\org\app\testapp\security\TokenProcessingFilter.class]:
Invocation of init method failed; nested exception is
java.lang.IllegalArgumentException: authenticationManager must be
specified
You need to set the AuthenticationManager on TokenProcessingFilter. Instead of using #Component on TokenProcessingFilter, just create it in the SecurityConfig.
#Bean
TokenProcessingFilter tokenProcessingFilter() {
TokenProcessingFilter tokenProcessingFilter = new TokenProcessingFilter();
tokenProcessingFilter.setAuthenticationManager(authenticationManager());
return tokenProcessingFilter;
}
and
protected void configure(HttpSecurity http) throws Exception {
...
.addFilter(tokenProcessingFilter())
In order to keep Component annotation you have to override setAuthenticationManager from AbstractAuthenticationProcessingFilter and autowire the parameter like this:
#Component
public class TokenProcessingFilter extends UsernamePasswordAuthenticationFilter {
//...
#Override
#Autowired
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
//...
}