I'm trying to configure Spring Security on a Spring Boot application as follows:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private RestAuthenticationEntryPoint unauthorizedHandler;
#Bean
public JwtAuthenticationFilter authenticationTokenFilterBean() throws Exception {
JwtAuthenticationFilter authenticationTokenFilter = new JwtAuthenticationFilter();
authenticationTokenFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationTokenFilter;
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
//#formatter:off
httpSecurity
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(this.unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/login", "/singup", "/subscribers").permitAll()
.anyRequest().authenticated();
// Custom JWT based security filter
httpSecurity
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
//#formatter:on
}
}
My unauthorizedHandler is:
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static final Logger LOGGER = LoggerFactory.getLogger(RestAuthenticationEntryPoint.class);
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
Finally, the REST controller for /subscribers is:
#RestController
public class SubscriberRestController {
#Autowired
ISubscribersService subscribersService;
#RequestMapping(value = RequestMappingConstants.SUBSCRIBERS, method = RequestMethod.GET)
#ResponseBody
public Number subscriberCount() {
return subscribersService.subscribersCount();
}
#RequestMapping(value = RequestMappingConstants.SUBSCRIBERS, method = RequestMethod.POST)
public String subscriberPost(#RequestBody SubscriberDocument subscriberDocument) {
return subscribersService.subscribersInsert(subscriberDocument);
}
#RequestMapping(value = "/test", method = RequestMethod.GET)
public String test() {
return "This is a test";
}
}
I use postman to test endpoints and when I do a POST to "localhost:8080/subscribers", I get:
I want to have opened endpoints (/subscribers) without any security control or credentials check, endpoints for singup and login and secured endpoints for authenticated users.
Thanks! :)
Spring Boot was not applying the configuration because couldn't find it. On Application.java config package was not included with #ComponentScan anotation.
After some researching, here is solution:
#SpringBootApplication(exclude = {SecurityAutoConfiguration.class })
#ComponentScan(basePackages = { PackageConstants.PACKAGE_CONTROLLERS_REST, PackageConstants.PACKAGE_SERVICES,
PackageConstants.PACKAGE_SERVICES_IMPL, PackageConstants.PACKAGE_MONGO_REPOSITORIES,
PackageConstants.PACKAGE_MONGO_REPOSITORIES_IMPL, PackageConstants.PACKAGE_UTILS })
public class Application {
// Clase principal que se ejecuta en el bootrun
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Main line is #SpringBootApplication(exclude = {SecurityAutoConfiguration.class }) it tells not use Spring Boot Security AutoConfiguration configuration. It is not full answer, because now you have to tell Spring user your Spring Security configuration class. Also i advice you to create Initializer class with init Root Config Classes, ApplicationConfiguration using and refuse to use SpringBoot applications. Something like this:
ApplicationConfig:
#Configuration
#EnableWebMvc
#ComponentScan("com.trueport.*")
#PropertySource("classpath:app.properties")
public class ApplicationConfig extends WebMvcConfigurerAdapter {
....
}
ApplicationSecurityConfig:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter {
....
}
Initializer:
public class Initializer implements WebApplicationInitializer {
private static final String DISPATCHER_SERVLET_NAME = "dispatcher";
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
....
DispatcherServlet dispatcherServlet = new DispatcherServlet(ctx);
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
ctx.register(ApplicationConfig.class);
ServletRegistration.Dynamic servlet = servletContext.addServlet(DISPATCHER_SERVLET_NAME,
dispatcherServlet);
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
servlet.setAsyncSupported(true);
}
}
You need to add the following to your configure method /error is the default fall back when error occurs to the application due to any exception and it is secured by default.
protected void configure(HttpSecurity httpSecurity) throws Exception {
//disable CRSF
httpSecurity
//no authentication needed for these context paths
.authorizeRequests()
.antMatchers("/error").permitAll()
.antMatchers("/error/**").permitAll()
.antMatchers("/your Urls that dosen't need security/**").permitAll()
Also the below code snippet
#Override
public void configure(WebSecurity webSecurity) throws Exception
{
webSecurity
.ignoring()
// All of Spring Security will ignore the requests
.antMatchers("/error/**")
}
Now you will not get 401 and get 500 exception with details when an exception occurred for permitAll Urls
If your application is simply saving APIs, and you have included dependency for spring security - for any other reason (Mine was to enables headers X-Frame and Content-Security-Policy), then by default Spring includes servlet filter for csrf protection. If you do not disable this, all requests fail with HTTP 401 error.
To disable it, You create a Configuration class extending WebSecurityConfigurerAdapter and annotated with EnableWebSecurity
#EnableWebSecurity
#Configuration
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable();
}
}
This article is worthy the read - very detailed.
Related
I have a working Spring Boot with Spring Security. Everything mostly works. What I'm not understanding is why the RestController never fires following the filter authorizing the request.
In other words, I have a rest controller set up to accept POST requests from /foo/bar and I have an AuthenticationFilter set up to first verify the users credentials before performing what the user requested.
Given that my RestController never fires, I've had to implement my code in the Success Handler, but my code belongs in the Controller instead.
I've attempted to debug this by stepping through Spring Security code, but nothing appears to suggest it would skip my RestController.
#RestController
#RequestMapping("foo")
public class FooController {
#PostMapping("bar") // this never executes
public ResponseEntity<FooResponse> foobar(#RequestBody Credentials creds) {
return new ResponseEntity<>(new FooResponse("baz"), HttpStatus.OK);
}
}
#Configuration
#EnableWebSecurity
#RequiredArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public AuthenticationFilter authenticationTokenFilter() throws Exception {
AuthenticationFilter filter = new AuthenticationFilter();
filter.setAuthenticationManager(authenticationManager());
filter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandlerImpl());
return filter;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(accountService)
.passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/foo/bar").permitAll()
.and()
.addFilter(new AuthorizationFilter(properties, authenticationManager(), tokenService)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
public class AuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public AuthenticationFilter() {
super("/foo/bar");
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
Credentials creds = new ObjectMapper().readValue(request.getInputStream(), Credentials.class);
return getAuthenticationManager().authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
new ArrayList<>())
);
}
}
public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
// do nothing and prevent redirect to /login, /logout, etc
}
}
You are using http.cors(). Have you configured it elsewhere? If not, it probably considers you are not authenticated. Check this link out: https://docs.spring.io/spring-security/site/docs/5.0.x/reference/html/cors.html
I would suggest you to remove it if you are not going to use it
Also you have here an example of an easy security configuration: https://www.baeldung.com/spring-security-login
From my point of view, removing cors() should fix your problem (or configuring it the right way :)
Hi to all Spring Experts!
I have an issue that I'm trying to solve for a while but i think that i have reached a dead end.
So basically what I need, is to configure my Spring-Security (in Spring-Boot) to have two authentication mechanisms (one for Legacy JSP pages and one for REST APIs). So I followed the following post:
multiple authentication mechanisms in a single app using java config
It worked fine with one LDAP authentication provider. But then I tried to extend my LDAP connection to also obtain a ticket from a third party service (that will be used for future connections to other services), and there I had a problem.
So I created a new Authentication Token, Filter and Authentication provider, but the default UsernamePasswordAuthenticationFilter is being fired first, no matter what I do.
I tried to follow this post How to configure a custom filter programatically in Spring Security? and saw that the problem might be in the fact that my filter was extending UsernamePasswordAuthenticationFilter. So I removed this and tried to have a simple AbstractAuthenticationProcessingFilter, still - no luck.
I think the problem is in my WebSecurity configuration. Currently, with the following code I'm gonna share, the REST Api authentication is returning 405 - method not allowed and the legacy Login is stuck in an infinite loop and crashes, even before I hit "Login".
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true) //Enables #PreAuthorize on methods
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private LDAPConfigurationBean ldapBean;
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//HERE GOES LDAP CONNECTION STUFF
// Add the custom LDAP + Token provider to the Authentication provider chain
auth.authenticationProvider(new TicketAndLDAPAuthenticationProvider(authenticator,authoritiesPopulator));
// Creating an LDAP provider using the authenticator and the populator.
auth.authenticationProvider(new LdapAuthenticationProvider(authenticator,authoritiesPopulator));
}
#Configuration
#Order(1)
public static class ConfigureFilters extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.addFilterBefore(new TicketAndLDAPAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class);
}
}
//Management Endpoints Authorization
#Configuration
#Order(2)
public static class EndpointsWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/manage/health")
.authorizeRequests()
.anyRequest().permitAll();
}
}
//API Authentication+Authorization
#Configuration
#Order(3)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private RestAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private RestAuthSuccessHandler authSuccessHandler;
#Autowired
private RestAuthFailureHandler authFailureHandler;
#Autowired
private RestLogoutSuccessHandler logoutSuccessHandler;
private String LOGIN_PATH = "/api/authenticate";
private String USERNAME = "username";
private String PASSWORD = "password";
protected void configure(HttpSecurity http) throws Exception {
/*CSRF configuration*/
http.csrf().disable();
http
.antMatcher(LOGIN_PATH)
.authorizeRequests()
.anyRequest().permitAll();
http
.antMatcher("/api/**")
//Stateless session creation - no session will be created or used by Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.formLogin().permitAll()
.loginProcessingUrl(LOGIN_PATH)
.usernameParameter(USERNAME)
.passwordParameter(PASSWORD)
.successHandler(authSuccessHandler)
.failureHandler(authFailureHandler)
.and()
.logout().permitAll()
.logoutSuccessHandler(logoutSuccessHandler);
http
.authorizeRequests().anyRequest().authenticated();
}
}
//JSP Authentication+Authorization
#Configuration
#Order(4)
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
/*CSRF configuration*/
http.csrf().disable();
/*Static content*/
http
.authorizeRequests()
.antMatchers("/css*//**").permitAll()
.antMatchers("/images*//**").permitAll()
.antMatchers("/scripts*//**").permitAll()
.antMatchers("/fonts*//**").permitAll()
.antMatchers("/login*").anonymous();
/*Login / Logout configuration*/
http
.formLogin()
.loginPage("/login.htm").permitAll()
.defaultSuccessUrl("/index.htm?name=******")
.failureUrl("/login.htm?error=true")
.and()
.logout().permitAll()
.logoutSuccessUrl("/login.htm")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID");
/*URL roles authorizations*/
http
.authorizeRequests().anyRequest().authenticated();
}
}
}
As you can see, I am trying to configure my filter in the "Configure Filters" method - but I have also tried to configure it inside the adapters, with / without a #Bean annotation - all with no luck.
Filter:
public class TicketAndLDAPAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public TicketAndLDAPAuthenticationFilter() {
super("/*");
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
//Save the password for later
String username = request.getParameter("username");
String password = request.getParameter("password");
TicketAndLDAPAuthenticationToken token = new TicketAndLDAPAuthenticationToken(username,password,null);
return token;
}
}
Edit: forgot to add to the filter:
if ( request.getParameter( "username" ) == null || request.getParameter( "password" ) == null ) == null ) {
return null;
}
Now I get 405 in both login mechanisms.
Token:
public class TicketAndLDAPAuthenticationToken extends UsernamePasswordAuthenticationToken {
private AuthTicket otp;
private String restoredPassword;
public TicketAndLDAPAuthenticationToken( String username, String password, RestAuthLoginTicket otp ) {
super( username, password );
this.otp = otp;
}
public AuthTicket getOTP() {
return otp;
}
public AuthTicket getOtp() {
return otp;
}
public void setOtp(AuthTicket otp) {
this.otp = otp;
}
}
Provider:
public class TicketAndLDAPAuthenticationProvider extends LdapAuthenticationProvider {
#Autowired
TokenUtils tokenUtils;
public TicketAndLDAPAuthenticationProvider(LdapAuthenticator authenticator, LdapAuthoritiesPopulator authoritiesPopulator) {
super(authenticator, authoritiesPopulator);
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
TicketAndLDAPAuthenticationToken token = (TicketAndLDAPAuthenticationToken) super.authenticate(authentication);
token.setOtp(tokenUtils.getTicket(token));
return token;
}
#Override
public boolean supports(Class<?> authentication) {
return TicketAndLDAPAuthenticationToken.class.isAssignableFrom(authentication);
}
}
Thanks in advance!!
So I found the issue(s).
First of all, the right way to configure the authentication managers is not how I configured above because there is no antMatcher and this caused my resources and pages to be open to everybody.
Secondly, the problem that caused the infinite redirects and error 405 was that I haven't defined my filter to accept post.
After fixing that, my JSP login form and authentication mechanism worked fine, but the "/api" was redirecting to the login page instead of the resource.
What brings me to my final point - the http.formLogin() is creating a UsernamePasswordAuthenticationFilter. I have two of them - one for each login. So I had to add http.addFilterBefore() for each one of the logins, but with a different URL.
The "/api" url was again using the default redirects of Spring instead of what I have defined, so I had to override them.
These are the configurations and filters that are working for me:
Security Configuration:
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true) //Enables #PreAuthorize on methods
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private LDAPConfigurationBean ldapBean;
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//LDAP Stuff
TicketAndLDAPAuthenticationProvider ticketAndLDAPAuthenticationProvider = new TicketAndLDAPAuthenticationProvider(authenticator,authoritiesPopulator);
auth.authenticationProvider(ticketAndLDAPAuthenticationProvider);
LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(authenticator,authoritiesPopulator);
auth.authenticationProvider(ldapAuthenticationProvider);
}
//Management Endpoints Authorization
#Configuration
#Order(1)
public static class EndpointsWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/manage/health")
.authorizeRequests()
.anyRequest().permitAll();
}
}
//API Authentication+Authorization
#Configuration
#Order(2)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private RestAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private RestAuthSuccessHandler authSuccessHandler;
#Autowired
private RestAuthFailureHandler authFailureHandler;
#Autowired
private RestLogoutSuccessHandler logoutSuccessHandler;
private String LOGIN_PATH = "/api/authenticate";
protected void configure(HttpSecurity http) throws Exception {
/*CSRF configuration*/
http.csrf().disable();
http.addFilterBefore(new TicketAndLDAPAuthenticationFilter(LOGIN_PATH,authSuccessHandler,authFailureHandler), UsernamePasswordAuthenticationFilter.class);
http
.antMatcher("/api/**")
// Stateless session creation - no session will be created or used by Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.logout().permitAll()
.logoutSuccessHandler(logoutSuccessHandler);
http
.authorizeRequests().anyRequest().authenticated();
}
}
//JSP Authentication+Authorization
#Configuration
#Order(3)
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
private String LOGIN_PATH = "/login.htm";
#Override
protected void configure(HttpSecurity http) throws Exception {
/*CSRF configuration*/
http.csrf().disable();
http.addFilterBefore(new TicketAndLDAPAuthenticationFilter(LOGIN_PATH), UsernamePasswordAuthenticationFilter.class);
/*Static content*/
http
.authorizeRequests()
.antMatchers("/css*//**").permitAll()
.antMatchers("/images*//**").permitAll()
.antMatchers("/scripts*//**").permitAll()
.antMatchers("/fonts*//**").permitAll()
.antMatchers("/login*").anonymous();
/*Login / Logout configuration*/
http
.formLogin()
.loginPage(LOGIN_PATH).permitAll()
.defaultSuccessUrl("/index.htm?name=******")
.failureUrl("/login.htm?error=true")
.and()
.logout().permitAll()
.logoutSuccessUrl("/login.htm")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID");
/*URL roles authorizations*/
http
.authorizeRequests().anyRequest().authenticated();
}
}
}
And the Filter:
public class TicketAndLDAPAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public TicketAndLDAPAuthenticationFilter(String defaultProcessUrl) {
super(new AntPathRequestMatcher(defaultProcessUrl, "POST"));
}
public TicketAndLDAPAuthenticationFilter(String defaultProcessUrl, AuthenticationSuccessHandler authenticationSuccessHandler, AuthenticationFailureHandler authenticationFailureHandler) {
super(new AntPathRequestMatcher(defaultProcessUrl, "POST"));
setAuthenticationFailureHandler(authenticationFailureHandler);
setAuthenticationSuccessHandler(authenticationSuccessHandler);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
//Save the password for later
String username = request.getParameter("username");
String password = request.getParameter("password");
if ( username==null || password==null) {
return null;
}
TicketAndLDAPAuthenticationToken token = new TicketAndLDAPAuthenticationToken(username,password,null);
return token;
}
}
I have Spring Boot application. It is necessary to send Authorization header on every request otherwise deny access to resource.
But if I made request with this header once I can send requests without it and receive content of resource. Why? Where am I wrong in code?
Spring Security configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.rememberMe().disable()
.csrf().disable()
.authorizeRequests().anyRequest().fullyAuthenticated().and()
.httpBasic().and()
.formLogin().disable()
.logout().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("1").password("1").roles("USER");
}
}
Controller:
#RestController
public class FooController {
#RequestMapping("/foo")
public Bar bar() {
Bar bar = new Bar();
return bar;
}
}
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
Disable create session. Session Management
I have the following Sprring web app:
#Secured({"ROLE_ADMIN"})
#RequestMapping(value = "data/{id}", method = RequestMethod.GET)
public Object getData(#RequestPath String id)
#RequestMapping(value = "login", method = RequestMethod.GET)
public Object login(#RequestParam String username, #RequestParam String password)
In login I need to call another server, pass credentials and get back roles, then let spring know to use these roles for incoming user.
After login client can use getData method if pass authorization of ROLE_ADMIN.
How can I implement this behaviour using java config?
UPDATE:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public AuthenticationProvider authenticationProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
}
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
private static final Logger logger = LogFactory.getLogger();
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
log.debug("name=" + name + " password=" + password);
List<GrantedAuthority> grantedAuths = new ArrayList<>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
Authentication auth = new UsernamePasswordAuthenticationToken(name, password, grantedAuths);
return auth;
}
#Override
public boolean supports(Class<?> authentication) {
logger.debug("supports authentication=" + authentication);
return true;
}
}
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
}
But as I can see from the logs CustomAuthenticationProvider.authenticate is never called.
Did I miss something?
Thanks.
UPDATE 2: correct solution for me:
Remove Login url from authentication config
add exception handler to disable redirection in case of authentication error
add success handler to send user valid json response
use http POST for app/login
#EnableGlobalMethodSecurity(securedEnabled = true) in web config in order to allow #Secured annotation in controller.
Thanks for all prompts.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
**.anyRequest().authenticated()**
.and().formLogin()
.loginProcessingUrl("/login").usernameParameter("username")
.passwordParameter("password")
**.successHandler(authenticationSuccessHandler)**.failureHandler(authenticationFailureHandler)
.and().csrf().disable().**exceptionHandling()
.authenticationEntryPoint(errorsAuthenticationEntryPoint)**;
}
You need to use WebSecurityConfigurerAdapter like this:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.logout()
.logoutUrl("/myurl/logout")
.and()
.formLogin()
.loginPage("/myurl/login")
.defaultSuccessUrl("/myurl/login?success");
}
}
Every thing is explain in the documentation https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#jc-form
You will need to implement a custom AuthenticationProvider. Something like:
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void registerGlobalAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider());
}
#Bean
AuthenticationProvider customAuthenticationProvider() {
CustomAuthenticationProvider impl = new CustomAuthenticationProvider();
impl.setUserDetailsService(customUserDetailsService());
/* other properties etc */
return impl ;
}
#Bean
UserDetailsService customUserDetailsService() {
/* custom UserDetailsService code here */
}
}
I am wondering how can I make my "redirect:" workin with Spring Security.
All /auth* pathes work correctly. But when it cames up to [1] it just doesn't redirect. Spring Security 4.0.2.RELEASE, Spring MVC 4.0.8.RELEASE
#Controller
#RequestMapping(value = "/auth")
public class SomeAuthController {
#RequestMapping(value = "/external")
public String externalAuth(...) {
if(someCondition) return "redirect:" + someExternalUrl; // [1] https://external-service.com
else return "redirect:/"
}
}
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void registerGlobalAuthentication(AuthenticationManagerBuilder auth,
ShaPasswordEncoder shaPasswordEncoder,
List<AuthenticationProvider> authProviders)
throws Exception {
for(AuthenticationProvider provider : authProviders) auth.authenticationProvider(provider);
}
#Bean(name="myAuthenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/resources/**").permitAll();
http.authorizeRequests().antMatchers("/auth/**", "/").permitAll().anyRequest().authenticated();
http.formLogin()
.loginPage("/auth/login")
.loginProcessingUrl("/j_spring_security_check")
.usernameParameter("j_username")
.passwordParameter("j_password")
.failureUrl("/auth/login?error")
.permitAll();
http.logout()
.permitAll()
.logoutUrl("/auth/logout")
.logoutSuccessUrl("/")
.invalidateHttpSession(true);
}
}
Okay guys. Here's my answer. Hope it will help someone.
The first thing is to enable JSR250 in the security configuration bean.
#EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
Afterwards I added #PermitAll annotation for a method which contained redirecting.
#PermitAll
#RequestMapping(value = "/external")
public String externalAuth(...) {
if(someCondition) return "redirect:" + someExternalUrl; // [1] https://external-service.com
else return "redirect:/"
}
That's all. Have a nice debugging J