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);
}
//...
}
Related
I am using Spring Security to build an authentication entry point for my web app. Now mr registration works well aside for the fact that a user is unable to login due to a compilation error resulting from my successHandler() and failureHandler() method.
The error logged is: java.lang.Error: Unresolved compilation problems:
successHandler cannot be resolved to a variable
authenticationFailureHandler cannot be resolved to a variable
I am not sure what I am doing wrong. I am pasting the security configuration code of my spring boot app. Where do I need to add the required variable or parameters (if any) in order to resolve this?
I've tried to create 2 variables with private modifiers that denote to the same parameters of the Handler which still doesn't work
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
private DataSource dataSource;
#Value("${spring.queries.users-query}")
private String usersQuery;
#Value("${spring.queries.roles-query}")
private String rolesQuery;
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.
jdbcAuthentication()
.usersByUsernameQuery(usersQuery)
.authoritiesByUsernameQuery(rolesQuery)
.dataSource(dataSource)
.passwordEncoder(bCryptPasswordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/signup_employer").permitAll()
.antMatchers("/registrations").permitAll()
.antMatchers("/admin/**").hasAuthority("ADMIN").anyRequest()
.authenticated().and().csrf().disable()
.formLogin()
.loginPage("/login").failureUrl("/login?error=true")
.defaultSuccessUrl("/admin")
.usernameParameter("email")
.passwordParameter("password")
.successHandler(successHandler)
.failureHandler(authenticationFailureHandler)
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/logout").deleteCookies("JSESSIONID").deleteCookies("my-rememberme")
.logoutSuccessHandler(logoutSuccessHandler())
.and().rememberMe()
.tokenRepository(persistentTokenRepository())
.and()
// .exceptionHandling().accessDeniedHandler(accessDeniedHandler())
//.and()
.headers().cacheControl().disable()
.and().sessionManagement()
.sessionFixation().migrateSession()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.invalidSessionUrl("/invalidSession")
.maximumSessions(1)
.expiredUrl("/invalidSession");
}
#Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl tokenRepositoryImpl = new JdbcTokenRepositoryImpl();
tokenRepositoryImpl.setDataSource(dataSource);
return tokenRepositoryImpl;
}
#Bean
public LogoutSuccessHandler logoutSuccessHandler() {
return new CustomLogoutSuccessHandler();
}
#Bean
public AccessDeniedHandler accessDeniedHandler() {
return new CustomAccessDeniedHandler();
}
#Bean
public AuthenticationEntryPoint unauthorizedEntryPoint() {
return (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**", "/static/**", "/css/**", "/email_templates/**", "/error/**", "/font-awesome/**", "/fonts/**", "/res/**", "/vendor/**", "/js/**", "/img/**");
}
#Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
}
login success handler:
public class MySimpleUrlAuthenticationSuccessHandler implements
AuthenticationSuccessHandler {
protected final Log logger = LogFactory.getLog(this.getClass());
protected int SessionTimeout = 1 * 60;
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
public MySimpleUrlAuthenticationSuccessHandler() {
super();
}
// API
#Override
public void onAuthenticationSuccess(final HttpServletRequest request, final
HttpServletResponse response, final Authentication authentication) throws
IOException {
handle(request, response, authentication);
clearAuthenticationAttributes(request);
}
// IMPL
protected void handle(final HttpServletRequest request, final
HttpServletResponse response, final Authentication authentication) throws
IOException {
final String targetUrl = determineTargetUrl(authentication);
if (response.isCommitted()) {
logger.debug("Response has already been committed. Unable to
redirect to " + targetUrl);
return;
}
redirectStrategy.sendRedirect(request, response, targetUrl);
}
protected String determineTargetUrl(final Authentication authentication) {
boolean isUser = false;
boolean isAdmin = false;
final Collection<? extends GrantedAuthority> authorities =
authentication.getAuthorities();
for (final GrantedAuthority grantedAuthority : authorities) {
if (grantedAuthority.getAuthority().equals("USER")) {
isUser = true;
break;
} else if (grantedAuthority.getAuthority().equals("ADMIN")) {
isAdmin = true;
break;
}
}
if (isUser) {
return "/homepage.html";
} else if (isAdmin) {
return "/admin";
} else {
throw new IllegalStateException();
}
}
/**
* Removes temporary authentication-related data which may have been stored
in the session
* during the authentication process.
*/
protected final void clearAuthenticationAttributes(final HttpServletRequest
request) {
final HttpSession session = request.getSession(false);
if (session == null) {
return;
}
session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
}
protected RedirectStrategy getRedirectStrategy() {
return redirectStrategy;
}
public void setRedirectStrategy(final RedirectStrategy redirectStrategy) {
this.redirectStrategy = redirectStrategy;
}
}
Those two lines inside the configure(HttpSecurity) method refers to properties/variables which do not seem to exist.
.successHandler(successHandler)
.failureHandler(authenticationFailureHandler)
I see you've created your MySimpleUrlAuthenticationSuccessHandler. Provide an instance of that class to successHandler. And do the same with failureHandler with an instance of your custom/bundled AuthenticationFailureHandler.
I suppose that the warning you mention requires defining the AuthenticationSuccessHandler as Bean.
#Configuration
class MyConfigurationClass {
...
#Bean
AuthenticationSuccessHandler myAuthenticationSuccessHandler() {
return new MyCustomOrBundledAuthenticationSuccessHandler();
}
}
You can then
.successHandler(myAuthenticationSuccessHandler())
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'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();
}
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 just started using Spring boot and Java Based Spring Configurations and I have a problem when trying to test security-related methods such as login and logout.
I have the following configurations in my project
SecurityConfig.java:
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint())
.and()
.formLogin()
.successHandler(authenticationSuccessHandler())
.failureHandler(authenticationFailureHandler())
.and()
.logout()
.and()
.authorizeRequests()
.antMatchers("/login", "j_spring_security_check", "/register", "/logout").permitAll()
.antMatchers("/**").hasRole("USER");
}
#Autowired
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("guest").password("guest").roles("USER");
}
#Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() {
return new RestAuthenticationSuccessHandler();
}
#Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
return new SimpleUrlAuthenticationFailureHandler();
}
#Bean
public AuthenticationEntryPoint authenticationEntryPoint() {
return new RestAuthenticationEntryPoint();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
SecurityWebAppInitializer.java
#Order(1)
public class SecurityWebAppInitializer extends AbstractSecurityWebApplicationInitializer { }
RestAuthenticationSuccessHandler.java
public class RestAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private RequestCache requestCache = new HttpSessionRequestCache();
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
SavedRequest savedRequest = requestCache.getRequest(request, response);
if (savedRequest == null) {
clearAuthenticationAttributes(request);
return;
}
String targetUrlParam = getTargetUrlParameter();
if (isAlwaysUseDefaultTargetUrl() ||
(targetUrlParam != null &&
StringUtils.hasText(request.getParameter(targetUrlParam)))) {
requestCache.removeRequest(request, response);
clearAuthenticationAttributes(request);
return;
}
clearAuthenticationAttributes(request);
}
public void setRequestCache(RequestCache requestCache) {
this.requestCache = requestCache;
}
}
Now, I want to test the login process so my test class is the following:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { SecurityWebAppInitializer.class, SecurityConfig.class })
public class SecurityTests {
private MockMvc mockMvc;
#Autowired
private FilterChainProxy filterChainProxy;
#Before
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(SecurityWebAppInitializer.class, SecurityConfig.class).addFilters(filterChainProxy).build();
}
#Test
public void thatLoginWithCorrectCredentialsWorks() throws Exception {
mockMvc.perform(post("/j_spring_security_check")
.param("j_username", "guest")
.param("j_password", "guest")
).andExpect(status().isOk());
}
}
The above test returns the following error
java.lang.AssertionError: Status
Expected :200
Actual :403
which says that the user is forbidden to access the login page. However I don't get why as I've configured in the SpringConfig that a user with the above credentials is allowed to login to my application. I would like to apologise in advance in case my question is silly but I am still trying to improve my knowledge on that framework as I am using it only for a couple of weeks.
EDIT: When I am using CURL to login I am getting the following error:
{"timestamp":1401633376720,"error":"Forbidden","status":403,"message":"Expected CSRF token not found. Has your session expired?"}