Grails Spring Security AbstractPreAuthenticatedProcessingFilter redirect - spring

I am trying to do PreAuthentication using Spring Security Grails plugin. I read the pre authentication documentation given below, but could not find anything concrete for my situation
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/preauth.html
In my situation, we have a agent which parses the SAML request and gives a map after successful authentication. Recommendation is to use this jar. Hence, I extended AbstractPreAuthenticatedProcessingFilter and try to do this
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
userInfo = agent.readToken(request);
if (!userInfo){
WebUtils.retrieveGrailsWebRequest().getCurrentResponse().sendRedirect(ssoUrl)
}
return userInfo
}
I have placed myFilter under src/groovy and registered this filter in BootStrap
def init = { servletContext ->
SpringSecurityUtils.clientRegisterFilter(
'myFilter', SecurityFilterPosition.PRE_AUTH_FILTER.order)
}
Its getting loaded correctly, but filter is not issuing a redirect. First of all, I wanted to check if this is the right approach and if it is, how to get redirect working.
I have asked the same question in grails user forum
Any help is greatly appreciated.
Update:
Final configuration which worked for me
Wrote MyAuthenticationService which implements AuthenticationUserDetailsService as suggested. You also have to define preAuthenticatedAuthenticationProvider which wraps your custom service
resources.groovy
securityFilter(MySSOAuthFilters){ bean ->
authenticationManager = ref('authenticationManager')
grailsApplication = ref('grailsApplication')
}
customUserDetailsService(MyAuthenticationService)
preAuthenticatedAuthenticationProvider(org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider) {
preAuthenticatedUserDetailsService = ref('customUserDetailsService')
}
I was not able to do <form-login> because in Grails, this is done using
grails.plugins.springsecurity.auth.loginFormUrl config parameter which only accepts relative url.
What I ended up doing is grails.plugins.springsecurity.auth.loginFormUrl = '/login/index'
In LoginController
def index() {
if(springSecurityService.isLoggedIn()){
log.info("User is logged in")
return redirect(controller: 'mycontroller', action: 'list')
}
log.info("user is not logged in...redirect to sso.")
return redirect(url: ssoUrl)
}
Hope this helps

A couple of things I see that need to be changed.
First, do not to send a redirect in the preauth filter just simply return null. The preauth filter is only used to return a subject that your AuthenticationUserDetailsService can use to create the UserDetails object by implementing the method below in your implementation of the AuthenticationUserDetailsService .
public UserDetails loadUserDetails(AbstractAuthenticationToken token) {
return createUserFromSubject((Subject) token.getPrincipal());
}
Second, set the form login page as part of your configuration. This will be used to redirect to if no subject exists.
<form-login login-page="http://url_youwanttoredirect_to_on_auth_req" authentication-failure-url="http://url_youwanttoredirect_to_on_auth_req"/>

Related

Duplicate config code problem Spring Boot microservices

Is there a way to create a spring configuration class and use it to all my microservices?
For example, I have to copy the following configuration class and paste it through all my microservices which means that when I want to make a small change I have to go through all the microservices editing the same class.
I have investigated and the most I could find is to create a module with all the classes in common and import it in my microservices by the pom, what happens with this is that when I want to get the SecurityContextHolder.getContext() this is null for a context issue and I do not know very well how to give solution to this or what other alternatives there are.
#Configuration
public class FeignGlobalConfiguration {
#Bean
public ErrorDecoder errorDecoder() {
return new RetrieveMessageErrorDecoder();
}
#Bean
public RequestInterceptor requestInterceptor(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return requestTemplate -> {
requestTemplate.header(JwtClaims.USERNAME, authentication.getPrincipal().toString());
requestTemplate.header(JwtClaims.CLIENT, authentication.getDetails().toString());
requestTemplate.header(JwtClaims.TOKEN, authentication.getCredentials().toString());
requestTemplate.header(JwtClaims.ROLES, authentication.getAuthorities().toString());
};
}
}
The problem is your bean definition.
The line Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); is called when the bean is constructed so only once. After that the reference is used (which is probably null at the time of construction.
To solve move tha tline inside the lambda so it gets evaluated each time a request is processed.
#Bean
public RequestInterceptor requestInterceptor(){
return requestTemplate -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
requestTemplate.header(JwtClaims.USERNAME, authentication.getPrincipal().toString());
requestTemplate.header(JwtClaims.CLIENT, authentication.getDetails().toString());
requestTemplate.header(JwtClaims.TOKEN, authentication.getCredentials().toString());
requestTemplate.header(JwtClaims.ROLES, authentication.getAuthorities().toString());
};
}

Spring boot Authentication Failure Handler - Custom with no additional url or query parameters

My current implementation of Spring Boot Login is as follows:
login.html:
<div class="error-message m-l-4" th:if="${errorMessage}" th:text="${errorMessage}"></div>
WebSecurityConfigurerAdapter - configure method:
formLogin().failureUrl("/login-error")
RequestMapper:
#RequestMapping("/")
public class Root {
#GetMapping("login")
public ModelAndView getLoginView(ModelMap mav) {
return new ModelAndView("login", mav);
}
#GetMapping("login-error")
public RedirectView setErrorMessage(RedirectAttributes attributes) {
attributes.addFlashAttribute("errorMessage", "Username or Password is invalid");
return new RedirectView("login");
}
}
The above is working perfectly when the user inputs invalid credentials the error is displayed on HTML. However, is there a way to accomplish the same result but get rid of the extra directory /login-error? I don't want it to be accessible despite redirecting to the login page with the error. I have tried to translate a custom AuthenticationFailureHandler (.failureHandler(new CustomAuthenticationFailureHandler())) to have the same effect but I can only return JSON not HTML. Basically, I want the exact same behavior as the Instagram Login Page.
See Pages in Order and upon Refresh back to the exact page as Page 1:

How to handle login success in Spring Security

Apologies if my question seems like something a novice would ask. I am new to the Spring world. I am using Spring Security to authenticate users. Authentication is working properly, but after authentication success I want Spring to call the Controller method .`
#RequestMapping(value = "/login", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public JwtAuthenticationResponse userLogin() {
System.out.println("Login Success");
JwtUser user = (JwtUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String token = userService.generateToken(authenticationService.loadUserByUsername(user.getUsername()));
JwtAuthenticationResponse response = new JwtAuthenticationResponse(token, user.getAuthorities().toArray(),user.getUsername());
return response;
}
}
In Spring Security Configuration I added the following
http.authorizeRequests().anyRequest().permitAll().and().formLogin().loginProcessingUrl("/login").defaultSuccessUrl("/login");
http.addFilterBefore(filter,UsernamePasswordAuthenticationFilter.class);
In this case, it is returning a default Spring Login form. It is not calling my controller method.
I made the request using
http://localhost:8080/myapp/login
Can someone suggest what I have to do to invoke the Controller after login is successful, in order to send the Authentication token after login.
Appreciate any help!
Thank you
If you are providing your custom controller for login then just remove following
.formLogin().loginProcessingUrl("/login").defaultSuccessUrl("/login")
From httpSecurity configuration. And this will remove spring security's default login page.
or you can follow : http://docs.spring.io/spring-security/site/docs/current/guides/html5/form-javaconfig.html
I found the mistake I was doing. I need not declare request mapping for login. I realized that Spring will take care of it.

Authentication in Spring MVC via REST

I've been looking for a way to authenticate a user via REST controller (URL params).
The closest thing to do so is the following:
#Controller
#RequestMapping(value="/api/user")
public class UserController extends BaseJSONController{
static Logger sLogger = Logger.getLogger(UserController.class);
#RequestMapping(value = "/login", method = RequestMethod.POST)
public #ResponseBody String login(#RequestParam(value="username") String user, #RequestParam(value="password") String pass) throws JSONException {
Authentication userAuth = new UsernamePasswordAuthenticationToken(user, pass);
MyCellebriteAuthenticationProvider MCAP = new MyCellebriteAuthenticationProvider();
if (MCAP.authenticate(userAuth) == null){
response.put("isOk", false);
}
else{
SecurityContextHolder.getContext().setAuthentication(userAuth);
response.put("isOk", true);
response.put("token", "1234");
}
return response.toString();
}
}
However, this doesn't create a cookie.
Any idea or a better way to implement what I want to achieve?
Firstly, you should not do this manually:
SecurityContextHolder.getContext().setAuthentication(userAuth)
It is better to employ special filter responsible for authentication, setting security context and clearing it after request is handled. By default Spring Security uses thread locals to store security context so if you don't remove it after client invocation, another client can be automatically logged in as someone else. Remember that server threads are often reused for different request by different clients.
Secondly, I would recommend using basic or digest authentication for your RESTful web service. Both are supported by Spring Security. More in docs http://static.springsource.org/spring-security/site/docs/3.1.x/reference/basic.html
And finally, remember that RESTful web service should be stateless.
Also remember that Spring Security documentation is your friend. :-)

How to configure grails/spring authentication scheme per url?

How can I configure a grails application using Spring security such that one set of url's will redirect unauthenticated users to a custom login form with an http response code of 200, whereas another set of url's are implementing restful web services and must return a 401/not authorized response for unauthenticated clients so the client application can resend the request with a username and password in response to the 401.
My current configuration can handle the first case with the custom login form. However, I need to configure the other type of authentication for the restful interface url's while preserving the current behavior for the human interface.
Thanks!
If I understood right what you want to do, I got the same problem, before! but it is easy to solve it using Spring Security grails Plugin! So, first of all, you have to set your application to use basic authentication:
grails.plugins.springsecurity.useBasicAuth = true
So your restful services will try to login, and if it doesnt work it goes to 401!
This is easy but you also need to use a custom form to login right?! So you can just config some URL to gets into your normal login strategy like this:
grails.plugins.springsecurity.filterChain.chainMap = [
'/api/**': 'JOINED_FILTERS,-exceptionTranslationFilter',
'/**': 'JOINED_FILTERS,-basicAuthenticationFilter,-basicExceptionTranslationFilter'
]
So noticed, that above, everything that comes to the URL /api/ will use the Basic Auth, but anything that is not from /api/ uses the normal authentication login form!
EDIT
More information goes to http://burtbeckwith.github.com/grails-spring-security-core/docs/manual/guide/16%20Filters.html
I had the same issue and did not found a good solution for this. I am really looking forward a clean solution (something in the context like multi-tenant).
I ended up manually verifying the status and login-part for the second system, which should not redirect to the login page (so I am not using the "Secured" annotation). I did this using springSecurityService.reauthenticate() (for manually logging in), springSecurityService.isLoggedIn() and manually in each controller for the second system. If he wasn't, I have been redirecting to the specific page.
I do not know, whether this work-around is affordable for your second system.
You should make stateless basic authentication. For that please make following changes in your code.
UrlMappings.groovy
"/api/restLogin"(controller: 'api', action: 'restLogin', parseRequest: true)
Config.groovy
grails.plugin.springsecurity.useBasicAuth = true
grails.plugin.springsecurity.basic.realmName = "Login to My Site"
grails.plugin.springsecurity.filterChain.chainMap = [
'*' : 'statelessSecurityContextPersistenceFilter,logoutFilter,authenticationProcessingFilter,customBasicAuthenticationFilter,securityContextHolderAwareRequestFilter,rememberMeAuthenticationFilter,anonymousAuthenticationFilter,basicExceptionTranslationFilter,filterInvocationInterceptor',
'/api/': 'JOINED_FILTERS,-basicAuthenticationFilter,-basicExceptionTranslationFilter'
]
resources.groovy
statelessSecurityContextRepository(NullSecurityContextRepository) {}
statelessSecurityContextPersistenceFilter(SecurityContextPersistenceFilter, ref('statelessSecurityContextRepository')) {
}
customBasicAuthenticationEntryPoint(CustomBasicAuthenticationEntryPoint) {
realmName = SpringSecurityUtils.securityConfig.basic.realmName
}
customBasicAuthenticationFilter(BasicAuthenticationFilter, ref('authenticationManager'), ref('customBasicAuthenticationEntryPoint')) {
authenticationDetailsSource = ref('authenticationDetailsSource')
rememberMeServices = ref('rememberMeServices')
credentialsCharset = SpringSecurityUtils.securityConfig.basic.credentialsCharset // 'UTF-8'
}
basicAccessDeniedHandler(AccessDeniedHandlerImpl)
basicRequestCache(NullRequestCache)
basicExceptionTranslationFilter(ExceptionTranslationFilter, ref('customBasicAuthenticationEntryPoint'), ref('basicRequestCache')) {
accessDeniedHandler = ref('basicAccessDeniedHandler')
authenticationTrustResolver = ref('authenticationTrustResolver')
throwableAnalyzer = ref('throwableAnalyzer')
}
CustomBasicAuthenticationEntryPoint.groovy
public class CustomBasicAuthenticationEntryPoint extends
BasicAuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request,
HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
ApiController
#Secured('permitAll')
class ApiController {
def springSecurityService
#Secured("ROLE_USER")
def restLogin() {
User currentUser = springSecurityService.currentUser
println(currentUser.username)
}
}

Resources