Authentication on role management with Resteasy on JBoss7.1 - java

I do like to experiment on JBoss from a long time.
Now I am facing some problem during authentication on role management with Resteasy on JBoss7.1.
Let me explain the problem.
Just i started a simple Web Application on Jboss7 with implementation of Resteasy. I am able to login by authenticating the user-role. The Problem comes when i am trying to logout. I found during Login if you do securityContext.getUserPrinicials.getName() , The username is the output as that is authenticated. But there is no session is managed for that user. so what would be the best way to implement the Logout functionality. I am pretty new to Jboss7 and Resteasy both.. Apology if i have said anything wrong..
Thanks In Advance

I am also new to Resteasy .Servlet 3.0 module has something to do this.
I got some idea from here.
As you have not given your sample code i have tested this following code for logout that is working fine..
//import things
#Path("/userrealam")
public class UserService {
#Context HttpServletRequest request;
#Context HttpServletResponse response;
#GET
#Path("logout")
#Produces({MediaType.TEXT_PLAIN})
public void logout() throws JAXBException, IOException {
try {
if (request.getUserPrincipal() != null){
request.logout();
}
}
catch (Exception e) {
}
}
}

Related

Spring Security 5 - How to SSO from id_token (Google one-tap)

I'm working on an application with Spring Boot 5 and OIDC. I've configured OIDC with Google and it works fine. I'm redirected to login at Google and then it redirects me to the app creating a new session. Now I'm trying to use Google One-Tap. It works fine as well. When user clicks on the one-tap's modal to continue with his Google identity I receive a POST with the id_token. What I want to do is to create a Spring session from this POST as it is created when user is logged by OIDC.
I think the right way is to create a Filter extending AbstractAuthenticationProcessingFilter. I've found some references here and here but I don't have the access_token when receiving the one-tap POST:
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
...
OAuth2AuthenticationToken oauth2Authentication = new OAuth2AuthenticationToken(
authenticationResult.getPrincipal(),
authenticationResult.getAuthorities(),
authenticationResult.getClientRegistration().getRegistrationId());
oauth2Authentication.setDetails(authenticationDetails);
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
authenticationResult.getClientRegistration(),
oauth2Authentication.getName(),
authenticationResult.getAccessToken(),
authenticationResult.getRefreshToken());
this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, oauth2Authentication, request, response);
return oauth2Authentication;
}
Has anyone tried to do it before ?
I've finally found a solution. I've shared the code here. Hope it helps to anyone dealing with the same issue.

Spring Security - Public pages redirected to login with invalid session id

I came across an Issue where public urls won't work in Spring security, when you already have an SessionID which is not valid anymore.
Example:
I have the user-register page and gave it a permitAll access like so:
http.authorizeRequests().antMatchers("/register**").permitAll();
http.authorizeRequests().anyRequest().authenticated();
http.formLogin().loginPage("/login").permitAll();
For my Session settings i have:
http.sessionManagement().invalidSessionUrl("/login?logoutcause=sessiontimeout");
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
http.sessionManagement().sessionAuthenticationErrorUrl("/login");
http.sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true);
http.sessionManagement().sessionFixation().newSession();
If i have a sessionID from a previous session, which is maybe an old and invalid one, and i hit the Route "/register", spring complains about the invalid session ID and redirects me to "/login".
Just to mention it: Everything else, like login, ressource management, protected urls and logout is working as expected with the configuration.
Reproducing this: Use Redis-Session management in Spring. Got to login page, flush redis db with console. Access the register page directly in browser -> redirected to login because of invalid session id.
o.s.s.w.s.SessionManagementFilter : Requested session ID 8ad2e166-bc21-4646-8390-ad8d1043baec is invalid.
w.s.SimpleRedirectInvalidSessionStrategy : Starting new session (if required) and redirecting to '/login?logoutcause=sessiontimeout'
o.s.s.w.DefaultRedirectStrategy : Redirecting to '/login?logoutcause=sessiontimeout'
Why does Spring even check the session id for a route that have "public" access?
The next Step:
if i fully disable any security checks on the route itself, sadly the required ressources like js and css assets trigger the same behavior and either i get redirected to login, or the assets simply do not get delivered (both no option :D )
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/register/**");
super.configure(web);
}
My Solution and Workaround
I disabled the following config which solved all my problems
// DISABLED
// http.sessionManagement().invalidSessionUrl("/login?logoutcause=sessiontimeout");
My Question
This can not be the best way to do it, right?
What is the better and more secure way to do it.
Please help me to understand why this is done this way by spring, or what i configured the wrong way.
I have solved the same issue by adding the following config:
.antMatchers("/login-invalid").permitAll()
I had same issue, and solved like below.
0. pre-requisite
jdk 17
spring-boot 2.7
1. create custom InvalidSessionStrategy
#Component
public class MyInvalidSessionStrategy implements InvalidSessionStrategy {
private final InvalidSessionStrategy simpleRedirectStrategy;
private final InvalidSessionStrategy requestedUrlRedirectStrategy;
private final HandlerMappingIntrospector handlerMappingIntrospector;
public MyInvalidSessionStrategy(HandlerMappingIntrospector handlerMappingIntrospector) {
this.simpleRedirectInvalidSessionStrategy = new SimpleRedirectInvalidSessionStrategy("/login?logoutcause=sessiontimeout");
this.requestedUrlRedirectStrategy = new RequestedUrlRedirectInvalidSessionStrategy();
this.handlerMappingIntrospector = handlerMappingIntrospector;
}
#Override
public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response)
throws IOException {
var matcher = new MvcRequestMatcher(handlerMappingIntrospector, "/register/**");
if (matcher.matches(request))) {
requestedUrlRedirectStrategy.onInvalidSessionDetected(request, response);
} else {
simpleRedirectStrategy.onInvalidSessionDetected(request, response);
}
}
}
2. configure spring-securiry and register custom InvalidSessionStrategy
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Bean
SecurityFilterChain filterChain(
HttpSecurity http,
MyInvalidSessionStrategy invalidSessionStrategy)
throws Exception {
return http
.login(...)
.logout(...)
.sessionManagement(
configurer -> configurer
.invalidSessionStrategy(invalidSessionStrategy))
.build();
}
}

Attaching query parameters in a ClientRequestFilter

I simply need to attach query parameters onto an outgoing request. (Java EE 7.0, JAX-RS 2.0)
In specifics, I currently using the RESTeasy Client ver 3.0.14, so I make my calls using the fancy interface-proxy system. I was attempting to produce something like this:
myapplication/api/path?timestamp=000
with:
#Provider
public class MyRequestFilter implements ClientRequestFilter {
#Context
private HttpServletRequest servletRequest;
public void filter(ClientRequestContext requestContext) throws IOException {
servletRequest.getParameterMap().put("timestamp", new String[]{
String.valueOf(new Date().getTime())
});
}
}
I made sure I was registering it with client.register(MyRequestFilter.class) as well. Feel free to ask questions. Thanks!
Credit to #peeskillet --
Rebuild the URI from the requestContext like this:
requestContext.setUri(UriBuilder.fromUri(requestContext.getUri()).queryParam("key", value).build());
You can now see the new query parameter with
requestContext.getUri().toString();
Again, verify that you register it when making the REST Client
client.register(MyRequestFilter.class);

Jersey Async ContainerRequestFilter

I have a Jersey REST API and am using a ContainerRequestFilter to handle authorization. I'm also using #ManagedAsync on all endpoints so that my API can serve thousands of concurrent requests.
My authorization filter hits a remote service, but when the filter is run, Jersey hasn't yet added the current thread to it's internal ExecutorService, so I'm completely losing the async benefits.
Can I tell Jersey that I want this ContainerRequestFilter to be asynchronous?
#Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter
{
#Inject
private AuthorizationService authSvc;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
String authToken = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
// HITS A REMOTE SERVER
AuthorizationResponse authResponse = authSvc.authorize(authToken);
if (!authResponse.isAuthorized())
{
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED)
.entity("unauthorized!")
.build());
}
}
}
And here's an example resource:
#Path("/stuff")
#Produces(MediaType.APPLICATION_JSON)
public class StuffResource
{
#GET
#Path("/{id}")
#ManagedAsync
public void getById(#PathParam("id") long id, #Suspended final AsyncResponse ar)
{
Stuff s;
// HIT THE DATABASE FOR STUFF
ar.resume(s);
}
}
UPDATE Just heard back from the Jersey guys, and this is not possible as of 2.7. Only the resource method itself is invoked asynchronously, not filters. Any suggestions for proceeding still welcome.
This is not built in to Jersey as of 2.7.
#ManagedAsync is useless if you have any filters or interceptors that do any serious work (like hit a remote authorization service). They may add the ability to run filters asynchronously in the future, but for now you're on your own.
UPDATE - there are other ways...
After a long and perilous journey, I have found a very hacky solution that I'm using in the short term. Here is a rundown of what I tried and why it failed/worked.
Guice AOP - failed
I use Guice for DI (getting Guice injection to work with Jersey is a feat in itself!), so I figured I could use Guice AOP to get around the issue. Though Guice injection works, it is impossible to get Guice to create resource classes with Jersey 2, so Guice AOP cannot work with resource class methods. If you are trying desperately to get Guice to create resource classes with Jersey 2, don't waste your time because it will not work. This is a well-known problem.
HK2 AOP - RECOMMENDED SOLUTION
HK2 just recently released an AOP feature, see this question for details on how to get it working.
Monitoring - also worked
This is not for the faint of heart, and it is completely discouraged in the Jersey docs. You can register and ApplicationEventListener and override onRequest to return a RequestEventListener that listens for RESOURCE_METHOD_START and calls an authentication/authorization service. This event is triggered from the #ManagedAsync thread, which is the whole goal here. One caveat, the abortWith method is a no-op, so this won't work quite like a normal ContainerRequestFilter. Instead, you can throw an exception if auth fails instead, and register an ExceptionMapper to handle your exception. If someone is bold enough to give this a try, let me know and I'll post code.
I am not sure if this is what you were looking for but, have you looked into Spring's OncePerRequestFilter? I am currently using it for my authorization layer where each request goes through some filter that extends this OncePerRequestFilter depending on how my filters are mapped to the URLs. Here's a quick overview of how I am using it:
Authentication/Authorization of a resource in Dropwizard
I am not very clear on the async dispatch parts of these filters but I hope this link atleast sheds some light to what you are trying to achieve!
We use Spring security for authentication/authorization. I worked around the problem using a sub-resource locator with empty path as shown below:
#Path("/customers")
public class CustomerResource {
#Inject
private CustomerService customerService;
#Path("")
public CustomerSubResource delegate() {
final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return new CustomerSubResource(auth);
}
public class CustomerSubResource {
private final Authentication auth;
public CustomerSubResource(final Authentication auth) {
this.auth = auth;
}
#POST
#Path("")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#ManagedAsync
public void createCustomer(final Customer customer, #Suspended final AsyncResponse response) {
// Stash the Spring security context into the Jersey-managed thread
SecurityContextHolder.getContext().setAuthentication(this.auth);
// Invoke service method requiring pre-authorization
final Customer newCustomer = customerService.createCustomer(customer);
// Resume the response
response.resume(newCustomer);
}
}
}

Spring security really strange behaviour in IE

I'm having the weirdest problem I have ever seen before.
The application I am working on uses spring security 3.1.3 to provide authentication support. There is a custom login form for which I have implemented a custom authenticationmanager / successhandler and failurehandler.
For some reason on internet explorer I always get the error message "Please fill in all mandatory fields". This is caused by appending /login?error=1 to the end of my url which can only be accessed through the following code (the redirectAndAddError method):
public class TideUserNamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public TideUserNamePasswordAuthenticationFilter() {
super();
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
String username = request.getParameter(SPRING_SECURITY_FORM_USERNAME_KEY);
String password = request.getParameter(SPRING_SECURITY_FORM_PASSWORD_KEY);
if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
redirectAndAddError(response);
return null;
}
return super.attemptAuthentication(request, response);
}
private void redirectAndAddError(HttpServletResponse response) {
try {
response.sendRedirect("/tide/login?error=1");
} catch (IOException e) {
throw new AuthenticationServiceException(e.getMessage(), e);
}
}
So what I tried was using Fiddler2, a web debugging proxy to view if one of the two parameters are actually empty. The strange thing is that when this program is running the error does not occur anymore and I can log on successfully.
Had anyone had a similar problem before? I think it's not related to my code as running the tool suddenly "solves" the problem.
This problem only occurs in internet explorer which makes it even more strange.
Edit
I have used another tool to watch the requests and this is what happens in IE:
First a POST request is sent to the uri /authenticate, I have set this myself like this:
<beans:property name="filterProcessesUrl" value="/authenticate"/>
The response of that request has http status code 302, moved temporarily and returns that the new location is at /login?error=1 (my form with the mandatory fields required error).
After that a GET request occurs to /login?error=1 with status code 401: Unauthorized. The intercept-url is set up like this:
<intercept-url pattern="/login**" access="permitAll"/>
The next request is a GET request to /login?error=1 again, this time the status code is showing: ERROR_INTERNET_CONNECTION_RESET, which looks like it could be a problem.
In Google Chrome the following request is made:
POST to /authenticate, result is a 302: moved temporarily to the dashboard page (which I display after logging on)
Someone on my team finally figured out what the problem was after finding this issue in the chromium bugtracker:
https://code.google.com/p/chromium/issues/detail?id=62687
The problem has been resolved by adding this in our login controller
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String doLogin() throws ServletException, IOException {
return "forward:/authenticate";
}
and changing the url that the form posts to to this one instead of the authentication url that spring security provides (we are redirecting to it manually now)

Categories

Resources