I integrated an webapp that uses JSF 2 with Spring Security 3.2 and Spring 4.0 (compatible, see documentation, and this thread), using annotations, and I have this configuation:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/my-account", "**/myAccount.**").authenticated()
.and()
.formLogin().loginPage("/login").permitAll();
The login page is being showed correctly, but when I submit the username and password, JSF BakcingBean method is never called. I want to process some validations (required fileds, etc) on this method and throw exceptions (required field messages).
If I comment the line that setup my custom login page, the desired method is called.
This article, and this other, are examples of what I'm trying to do. Notice that the methods declared on the managed beans, apparently, are being called.
The question are: am I forgetting some configuration? How to do to Spring let JSF perform my validations, display required fields messages, etc?
People, after searching all this day on the internet I haven't found any example of this being doing using annotations.
I just migrated to XML files and now everything works.
I'm not an expert of Spring, but based in just what I tested, I think that using annotations Spring created that automatic filters that, for some reason, intercepted all the requests coming from the configured custom login form, blocking the JSF from handling the requests. This can be happening because some undocumented incompatibility between "Spring 4" and Spring "Security 3.2". This incompatibility doesn't occur when using XMLs.
If you create the security filters on the classic manual way on the web.xml, and configure your custom login form on Spring XML files, you can use the JSF features on the login form again.
Same security configuration of annotation migrated to XML and it worked.
PS: Sorry, I can't share the detailed files because this time it's not open source.
I answered this in the Spring Security JIRA at SEC-2761, but I'm posting here to help anyone else that stumbles across this issue.
The problem is that Java Configuration defaults the login processing URL to be a POST to the value of the login form. This means since the login page is configured to be loginPage("/login") a POST to /login will be intercepted by Spring Security.
To avoid this problem, you can either
perform a POST to a different URL and have the LoginController process that URL.
configure Spring Security to intercept a different URL using .loginProcessingUrl("/j_spring_security_check")
An example configuration for option 2 can be seen below:
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/j_spring_security_check")
...
}
I attached a working sample application to the previously mentioned JIRA. You can download it here.
Related
I need to use Keycloak and prefer to use stateless JWT tokens with my Spring Boot application. I can get it to run OK with sessions, but when converting this I need help
Forcing Spring Boot Security to check for logins
Allow a /logout URL (that goes to Keycloak)
My code "runs", but when I hit the initial page, I'm seeing log messages that seem to suggest it did not detect any sign of being logged in. When this happens, I'd like to force Spring Boot to redirect to the login page just like it would have had this been a stateful application.
org.springframework.web.servlet.FrameworkServlet: Failed to complete request: java.lang.NullPointerException: Cannot invoke "org.springframework.security.authentication.AbstractAuthenticationToken.getName()" because "authenticationToken" is null
org.springframework.security.web.context.HttpSessionSecurityContextRepository$SaveToSessionResponseWrapper: Did not store anonymous SecurityContext
org.springframework.security.web.context.SecurityContextPersistenceFilter: Cleared SecurityContextHolder to complete request
org.apache.juli.logging.DirectJDKLog: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException: Cannot invoke "org.springframework.security.authentication.AbstractAuthenticationToken.getName()" because "authenticationToken" is null] with root cause
java.lang.NullPointerException: Cannot invoke "org.springframework.security.authentication.AbstractAuthenticationToken.getName()" because "authenticationToken" is null
Here's my HttpSecurity snippet:
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.csrf()
// .disable().exceptionHandling().accessDeniedPage("/access-denied")
.and()
.authorizeRequests()
.antMatchers("/sso/**").permitAll()
.antMatchers("/error/**").permitAll()
.antMatchers("/css/**","/contact-us","/actuator/**","/isalive/**").permitAll()
.anyRequest().authenticated()
.and()
.oauth2Login()
.defaultSuccessUrl("/myfirstpage",true)
.and().exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
.and()
.oauth2ResourceServer().jwt();
}
I know I'm missing something, but I thought Keycloak provided a lot of these things OOTB. The initial URL is /. I had hoped the .authenticated() would force it to authenticate against all patterns not permitted, but I'm likely wrong. What am I missing?
Please note, the internet is awash with examples of Spring Boot + Keycloak (a few are even good). It also has a lot of Spring Boot + OAuth + Stateless JWT. It does not have (that I can tell) a lot of Spring Boot + Keycloak + Stateless JWT. I got the little that I could find from this JHipster repo, but I feel like I'm missing some grand magical step.
Resource-server should return 401 (unauthorized) when authentication is missing or invalid (expired, wrong issuer, etc.) and client should handle redirection to authorization-server.
Things get messy when you try to merge different OAuth2 actors (client, resource-server and authorization-server) into a single application.
Are you sure you want a Spring client (and not an Angular / React / Vue / Flutter / whatever client-side rendering framework)?
If yes, maybe should you start by splitting client (presenting login, logout and Thymeleaf or whatever pages) and resource-server (REST API) apps. You'll better understand spring-security conf you write (and can assert it works as expected individually).
Resource-server configuration (REST API)
Be aware that Keycloak adapters for spring are deprecated.
Easiest solution is spring-addons-webmvc-jwt-resource-server (support multi-tenancy, stateless by default, CORS configuration from properties, easy Keycloak roles mapping to Spring authorities and more).
You can also work directly with spring-boot-starter-oauth2-resource-server, but it requires more java conf.
Client configuration (pages including login & logout)
if keeping Spring, refer to spring-boot documentation: it's clear and always up to date (as opposed to most tutorials)
if using a "modern" client-side framework, find a lib from certified list
I have a complete sample (Ionic-Angular UI with spring RESTful API) there, but it might be a little complicated as starter.
You will need spring-security and keycloak-adapters
This guid has full explanation how to setup and secure it
https://keepgrowing.in/java/springboot/keycloak-with-spring-boot-1-configure-spring-security-with-keycloak/
I am building a Spring Boot application using version 2.3.4 with spring-boot-starter-oauth2-client and spring-boot-starter-security dependencies.
I am trying to implement the JIRA Tempo plugin OAuth support.
I have it partially working using the following properties:
spring.security.oauth2.client.registration.tempo.redirect-uri=http://localhost:8080
spring.security.oauth2.client.registration.tempo.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.tempo.client-id=<the-client-id>
spring.security.oauth2.client.registration.tempo.client-secret=<the-client-secret>
spring.security.oauth2.client.registration.tempo.provider=tempo
spring.security.oauth2.client.provider.tempo.authorization-uri=https://mycompany.atlassian.net/plugins/servlet/ac/io.tempo.jira/oauth-authorize/?access_type=tenant_user
spring.security.oauth2.client.provider.tempo.token-uri=https://api.tempo.io/oauth/token/
and this config:
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests(expressionInterceptUrlRegistry -> expressionInterceptUrlRegistry.anyRequest().authenticated())
.oauth2Login();
}
When I access http://localhost:8080, it redirects to JIRA/Tempo and shows the approval dialog there to grant access to the Tempo data for my application. I can grant access, but after that, it just redirects again to that page instead of showing my own application.
With debugging, I noticed that there is a redirect to http://localhost:8080/?code=.... but Spring Security is not handling that. What else do I need to configure?
I also tried to set some breakpoints in DefaultAuthorizationCodeTokenResponseClient, but they are never hit.
UPDATE:
I changed the redirect-uri to be:
spring.security.oauth2.client.registration.tempo.redirect-uri={baseUrl}/{action}/oauth2/code/{registrationId}
(and I changed the Redirect URIs setting in Tempo to http://localhost:8080/login/oauth2/code/tempo).
This now redirects back to my localhost, but it fails with authorization_request_not_found.
UPDATE 2:
The reason for the authorization_request_not_found seems to be mismatch in HttpSessionOAuth2AuthorizationRequestRepository.removeAuthorizationRequest(HttpServletRequest request) between what is in the authorizationRequests and the stateParameters.
Note how one ends with = and the other with %3D, which makes them not match. It is probably no coincidence that the URL encoding of = is %3D. It is unclear to me if this is something that is a Spring Security problem, or a problem of the Tempo resource server, or still a misconfiguration on my part.
The redirect-uri property should not point to the root of your application, but to the processing filter, where the code after redirect is processed.
Best practice for you would be to leave the redirect-uri alone for the time being. Then it will default to /login/oauth2/code/* and this Url is handled by org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter.
We have a spring boot application configured as an oauth2 client. Occasionally, we have people where their browser sends a request like this to the application:
https://app/login?code=XXX&state=ZZZ
The code and state were cached from a previous authentication attempt and are invalid right now.
Spring security sees that this person is not authenticated, so it redirects them to /login which then does the whole oauth2 authentication but then after they are authenticated, spring security sends them back to /login?code=XXX&state=ZZZ because that was their original request. When that happens, it tries to validate the code and state but fails and sends them to an error page. This is a problem when supporting the app because the user is authentcated.
Is there a way to change the logic of the the storing of the initial request so that if it is /login we can replace that with /? There might be other solutions we haven't thought of. Any help would be appreciated.
Our app is currently using Spring boot 2 but I've tried this with the latest version of Spring boot 3 and it is still an issue. We have been unable to change the browser behavior so would like to solve this on the server if possible.
Here is our configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/info", "/static/**").permitAll()
.anyRequest().authenticated().and()
.csrf();
}
If I understand you correctly, you want to avoid redirect only sometimes (so SpringSecurity's defaultSuccessUrl is not an option).
If so, you can implement your own AuthenricationSuccessHandler like this:
...
.successHandler(
(request, response, authentication) -> {
if (request.getRequestURI().equals("/your/invalid/path"))
response.sendRedirect("/");
}
...
All http security is applied at startup:
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("admin")
}
During runtime, I am trying to add more to it ...
applicationContext.getBean(WebSecurityConfigurerAdapter).http.authorizeRequests().antMatchers("bla").hasRole("admin")
When that line is executed, it adds it to http.authorizeRequests()'s but /bla is still accessible by "non admins"
When server is restarted, this change takes effect because it is loading bla from the database.
How do I make the security take effect instantly without restarting the server?
You are trying to dynamicaly change a spring bean at runtime which is very hard to do unless you use tools like spring-loaded or JRebel.
There is a lot of SO about it :
Update spring beans dynamically. Is it possible?
dynamically change spring beans
Can I replace a Spring bean definition at runtime?
The best approach (in my opinion) for your use case is to use spring profiles.
Define a bean with authorisations for /bla and another bean without. Then use them in different profiles.
see dynamically declare beans at runtime in Spring
My solution to these case scenarios is to make a dynamic custom spring security rule to match with all the path.
http
.authorizeRequests()
.antMatchers("/**").access("#customSecurityRule.check(authentication)");
This way new endpoint will automatically be configured with our custom security rule, and in our custom security rule we can preety much do anything we want, checking their roles, validate it againts our database and etc.
I am trying to define and secure a RESTful API using Spring Boot. Ideally, I would like to use Spring Social and allow clients (web and mobile) to login via Facebook.
What is working
So far, I managed to have a working API using #RestController and secure it with a basic Spring Security configuration as follows:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/**").authenticated()
.antMatchers(HttpMethod.PUT, "/api/**").authenticated()
.antMatchers(HttpMethod.DELETE, "/api/**").authenticated()
.anyRequest().permitAll()
.and().httpBasic()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
The antMatchers could probably be improved, but I made it like this for my own clarity for now and it works fine. Doing GET requests is allowed and all others required to send the standard user:password given by Spring Security at runtime. An example using httpie:
http POST user:a70fd629-1e29-475d-aa47-6861feb6900f#localhost:8080/api/ideas/ title="My first idea"
Which right credentials, it sends a 200 OK back, otherwise a 401 Unauthorized.
Spring Social
Now, I am stuck and can't get my head around using Spring-Social-Facebook to get working with my current setup and keep fully RESTful controllers. Using standard forms and redirects seems trivial, but I couldn't find any solution for a REST-based approach that easily supports web and mobile clients for example.
As I understand, the client will have to handle the flow, since the back-end won't send any redirects to the /connect/facebook URL.
I followed the tutorial Accessing Facebook Data and it works on its own. However, I would like to avoid having to have those facebookConnect.html and facebookConnected.html templates like in the tutorial. So I don't know how to have change that.
Another Spring Boot tutorial for OAuth is also nice and working, but I would like to stick with Spring Social if possible due to the simplicity.
This post, helped for the Method not allowed issue of the /connect/facebook redirect when using those views mentioned above.
Post about Social Config. Probably, I am missing something there.
Any advice, solution or link to a better tutorial would be really helpful.
Thanks!
UPDATE 1
Now, I have a working website with traditional User SignUp and Login over forms. I have a "Login with Facebook" button that sends me over the "OAuth dance". So next issue is that I have to create somehow the User manually after the Facebook login has been successful, because for the moment both "logins" are not related, so even though the user is logged in with Facebook, he doesn't yet have an associated User object with the right authorisations.
SocialAuthenticationFilter by default, redirects to '/signup' in the case you described, user is signed in from a social app, however, no local account exists. You can provide a handler to create a local account. This is also covered in the spring-socal samples.
#RequestMapping(value = { "/signup" }, method = RequestMethod.GET)
public String newRegistrationSocial(WebRequest request, Model model) throws Exception {
String view = "redirect:/home";
try {
Connection<?> connection = providerSignInUtils.getConnectionFromSession(request);
if (connection != null) {
UserProfile up = connection.fetchUserProfile();
registerUser(up.getFirstName(), up.getLastName(), up.getEmail(), "DummyPassword");
providerSignInUtils.doPostSignUp(up.getEmail(), request);
//SignInUtils.signin(up.getEmail());
...
...
}
}
return view;
}