Creating Custom AuthenticationSuccessHandler and calling default AuthenticationSuccessHandler when done - spring

i want to create CustomAuthenticationSuccessHandler to add log to each user login:
class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler{
UserService getUserService() {
return ApplicationContextHolder.getBean('userService')
}
#Override
public void onAuthenticationSuccess(HttpServletRequest req,
HttpServletResponse res, Authentication auth) throws IOException,
ServletException {
userService.postLoginMessage(auth.principal.username, null)
}
}
it's simple enough code and works well, but now i have no return value. is there a way to call the default AuthenticationSuccessHandler after my custom code run or to return default value?
I mean to return the Authentication json.

You're doing it wrong essentially.
Instead of overriding the success handler, you should register an event listener.
Config:
grails.plugin.springsecurity.useSecurityEventListener = true
Register a class as a bean
class MySecurityEventListener
implements ApplicationListener<InteractiveAuthenticationSuccessEvent> {
#Autowired
UserService userService
void onApplicationEvent(InteractiveAuthenticationSuccessEvent event) {
userService.postLoginMessage(event.authentication.principal.username, null)
}
}

Related

how to override default error messages on spring-security

I'm using DaoAuthenticationProvider to provide authentication to my client requests. It is working fine in case the username/password combination is invalid it throws an AuthenticationException with a message: Bad credentials
This is good and expected behavior, but I'm trying to have more friendly messages so i would like to replace it with an error message of my own.
I found that this message comes from
public SpringSecurityMessageSource() {
setBasename("org.springframework.security.messages");
}
//a bunch of authentication code
messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials","Bad credentials")
I tried to replace this message by creating a file
resources/org/springframework/security/messages.properties
and having its content as: AbstractUserDetailsAuthenticationProvider.badCredentials=anything else
but the bad message is still being thrown... what i am doing wrong? how to redefine default org.springframework.security.messages
Here's what you can try using AuthenticationEntryPoint:
Create a class implementing AuthenticationEntryPoint then modify .write(..) according to your desired format and message:
public class MyEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Set your custom message here");
}
}
Set the custom entry point in your security config:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling()
.authenticationEntryPoint(new MyEntryPoint());
}
Find Spring Security class, which messages you need to override, it will have such field:
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
This class also should implement MessageSourceAware interface. This interface have only one method that you need to use: void setMessageSource(MessageSource messageSource)
For example I use DaoAuthenticationProvider. It extends AbstractUserDetailsAuthenticationProvider, that implements MessageSourceAware.
From Spring Security source code:
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
...
}
public abstract class AbstractUserDetailsAuthenticationProvider
implements AuthenticationProvider, InitializingBean, MessageSourceAware {
...
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
...
#Override
public void setMessageSource(MessageSource messageSource) {
this.messages = new MessageSourceAccessor(messageSource);
}
...
}
So, I'm overriding default DaoAuthenticationProvider and set my message source.
My code:
#Configuration
#EnableWebSecurity
#RequiredArgsConstructor // lombok
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final MessageSource messageSource;
private final UserDetailsService userDetailsService;
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("messages", "org/springframework/security/messages"); // my messages will override spring security messages, if message code the same
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Bean
public DaoAuthenticationProvider authProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService); // set my custom user details service
provider.setMessageSource(messageSource); // set my custom messages
return provider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authProvider()); // set dao provider with my custom messages
}
}
My overriden messages /src/main/resources/messages.properties:
AbstractUserDetailsAuthenticationProvider.disabled=Account is not activated. Please, activate your account. The activation link is sent in email
...etc...
All available codes for messages you can find here:
org.springframework.security:spring-security-core:[version]
/org/springframework/security/messages.properties

spring boot login check implement way

My way of implementing login check
#Configuration
public class ViewControllerImpl implements WebMvcConfigurer {
#Bean
public WebMvcConfigurer webMvcConfigurer(){
WebMvcConfigurer adapter = new WebMvcConfigurer() {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new InterceptorConfig()).addPathPatterns("/**")
.excludePathPatterns("/login", "/", "/session", "/static/**");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
WebMvcConfigurer.super.addResourceHandlers(registry);
}
};
return adapter;
}
}
public class InterceptorConfig implements HandlerInterceptor
{
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
{
Object loginStatus = request.getSession().getAttribute("loginStatus");
if(loginStatus == "success"){
return true;
}
else {
// request.getRequestDispatcher("login").forward(request, response);
response.sendRedirect("/login");
return false;
}
}
}
After the username and password are successfully verified,I will store a loginstatus in the session.
if loginstatus is success, it means that you have logged in.
Is this code implementation safe?
Do I need to use spring security?
Like #m-deinum already said you should really just utilize Spring-Security. There is a super quick tutorial on how to do this here: https://spring.io/guides/gs/securing-web/
You will start to learn it with BasicAuth and an InMemoryDB for user and password storage. You will end up with an application that returns a status Code 200 for logged in user or a status code 401 for a denied access.
From there on it is easy to extend your application to user user and password stored in a custom db or even OAuth.
Have no fear using it - its easy and fun :)

How to not use aspect in spring to write the request param and response param to the console

I found the class InvocableHandlerMethod.invokeForRequest will get the request param from request and invoke the controller class to get the return value.
What should I modify the method to write the params to the console?
I want to extends ServletInvocableHandlerMethod and override the method invokeForRequest but I can't call getMethodArgumentValues because it is private.should I copy the class of ServletInvocableHandlerMethod and InvocableHandlerMethod to modify the private method? Or is there have another way to log the request and response params without aspect?
Just create interceptor
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class LoggingInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// log here
return true; // let go further
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// log here
}
}
and register it
// example for Spring MVC annotation-based configuration
public class YourWebConfig extends WebMvcConfigurer {
...
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor());
}
...
}

Pre-conditions within #RequestMapping method?

I don't know how to redirect user if they do not meet certain preconditions for a #RequestMapping.
I have a simple form that after completion sends the user to "/secondForm" which is unrelated to "/firstForm", how can I restrict access to "/secondForm", if first form has not been completed?
What makes this more difficult for me there is a controller in the middle.
firstForm --- (submit)---> emailController ----(redirect)----> secondForm
If you want to redirect a user to another page when certain conditions are met, you can use an interceptor. Example interceptor class:
#Component
public class RedirectInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object o, ModelAndView modelAndView) {
if (request.getRequestURI().contains("secondForm") && modelAndView.getModel().get("someBoolean") == false {
try {
response.sendRedirect("/firstForm");
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
}
}
And register it in configuration class:
#Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
#Autowired
RedirectInterceptor redirectInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(redirectInterceptor);
}
}
If you prefer xml configuration over java then you can alternatively use this in your spring config xml:
<mvc:interceptors>
<bean id="redirectInterceptor" class="path.to.your.interceptor.RedirectInterceptor"/>
</mvc:interceptors>
Found an additional way:
Using FlashAttribute, by assigning before the redirect in emailController that sets a value.
In applicationController, by using an if statement if there isn't a FlashAttribute then redirect is called to the root of the application.

Spring security CustomPermissionEvaluator not working

MethodSecurityConfig.java
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled=true)
#ComponentScan(basePackageClasses={EventWritePermissionEvaluator.class})
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration{
private EventWritePermissionEvaluator eventWritePermissionEvaluator;
#Autowired
public void setEventWritePermissionEvaluator(
EventWritePermissionEvaluator eventWritePermissionEvaluator) {
this.eventWritePermissionEvaluator = eventWritePermissionEvaluator;
}
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler=new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(eventWritePermissionEvaluator);
return expressionHandler;
}
}
CustomPermissionEvaluator
#Component
public class EventWritePermissionEvaluator implements PermissionEvaluator{
private ChecklistService checklistService;
private UserService userService;
#Autowired
public void setChecklistService(ChecklistService checklistService) {
this.checklistService = checklistService;
}
#Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public CustomUser currentUser()
{
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
CustomUser customUser=(CustomUser) userService.loadUserByUsername(auth.getName());
return customUser;
}
#Override
public boolean hasPermission(Authentication authentication,
Object targetDomainObject, Object permission) {
Checklist checklist=(Checklist) targetDomainObject;
Event event=checklistService.getChecklist(checklist.getId()).getEvent();
String grp=event.getCreator().getGrp();
System.out.println("event grp:"+grp);
System.out.println("user grp:"+currentUser().getGrp());
if(currentUser().getGrp().equals(grp))
return true;
else
return false;
}
#Override
public boolean hasPermission(Authentication authentication,
Serializable targetId, String targetType, Object permission) {
return true;
}
}
ServiceMethod
#PreAuthorize("hasPermission(#ch,'write')")
public Map<String, Object> updateState(Checklist ch, HttpServletRequest request, HttpServletResponse response) throws MessagingException
{
}
The hasPermission() methods which i wrote in permissionEvaluator class are not getting invoked for incoming requests to service layer. Did i wrote anything wrong? i wrote some console statements in hasPermission() methods to see their execution. but i did not see anything in the console.
Thanks
What are you trying to achieve? It seems like I can achieve exactly the same thing just by using UserDetailsService implementation from latest Spring Security.
This is my blog post about it
Implementing UserDetailsService:
http://www.yjsblog.com/2015/10/05/how-to-implement-custom-spring-security-authentication-with-userdetailsservice/
Implementing Role as property on an entity:
http://www.yjsblog.com/2015/10/05/userdetails-role-from-database-or-as-an-entity-property/
Please have a look at above links.
Cheers,

Resources