Spring default Authentication internal working - java

I just started learning Spring and Spring Security and I have created a simple project by reading Spring Security documentation. I done the following java based configuration.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("admin")
.password("nimda")
.roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/admin**").access("hasRole('ADMIN')")
.and().formLogin();
http.csrf().disable();
}
}
When I go for "/admin" it redirect me to the login page which I know spring generated with this default configuration and after login it will show the login page. Now my question is: login form is posted to "/login" and I did not defined any "AuthenticationManager" and "UserDetailService" which I read in documentation for custom configuration then how spring post the form and do the login process? Basically I want to know some detail of inner working of this default login process.

When you use the *ConfigurerAdapter classes, there is a lot of stuff that goes on during context load. Spring will check if you have defined an AuthenticationManager, if you have not it will create a default one.
If you are really interested in what happens during the magic configuration step, you will probably have to look at the source code. For instance if you look at WebSecurityConfigurerAdapter.getHttp() you can see it calls authenticationManager() in order to construct this bean.
protected AuthenticationManager authenticationManager() throws Exception {
if (!authenticationManagerInitialized) {
configure(localConfigureAuthenticationBldr);
if (disableLocalConfigureAuthenticationBldr) {
authenticationManager = authenticationConfiguration
.getAuthenticationManager();
}
else {
authenticationManager = localConfigureAuthenticationBldr.build();
}
authenticationManagerInitialized = true;
}
return authenticationManager;
}
In the old days you had to create all of the beans, yourself and wire them together, so we were more aware of how things fitted together. Now you either have to read the source, or copy from a guide, and hope you don't make any mistakes.
Debug tip: These days I look at the beans that exist after the context is loaded, and then I go back and set a breakpoint in the constructor of the AuthenticationManager implementation, then I can see the call-stack and how the initialisation work.

Here is the code you are looking for, instead of using the builder to build an in-memory user details source, you can implement your own custom AuthenticationProvider.
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new AuthenticationProvider() {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String password = (String) authentication.getPrincipal();
String userName = (String) authentication.getCredentials();
if ("user".equals(userName) && "password".equals(password)) {
authentication = new UsernamePasswordAuthenticationToken(userName, password, Lists.newArrayList(new SimpleListProperty<GrantedAuthority>(null, "USER")));
return authentication;
}
throw new BadCredentialsException("Incorrect username or password.");
}
#Override
public boolean supports(Class<?> authentication) {
return true;
}
});
}
Be aware that you can create your own Authentication implementation in case you need to add additional information, or you can use the details property on that every Authentication can have.

Related

Authentication using cookies in spring boot

//Part of my Controller class
#RequestMapping("/login")
public String login(HttpServletRequest request,HttpServletResponse response) {
request.setAttribute("mode", "MODE_LOGIN");
return "welcomepage";
}
#RequestMapping ("/login-user")
public String loginUser(#ModelAttribute User user, HttpServletRequest request,HttpServletResponse response) {
if((userService.findByUsernameAndPassword(user.getUsername(), user.getPassword())!=null)) {
Cookie loginCookie=new Cookie("mouni","user.getUsername()");
loginCookie.setMaxAge(30*5);
response.addCookie(loginCookie);
return "homepage";
}
else {
request.setAttribute("error", "Invalid Username or Password");
request.setAttribute("mode", "MODE_LOGIN");
return "welcomepage";
}
}
I am doing a library management project on java spring boot.
I have one problem, i would like to do authentication using cookies.
In brief, Once after user logged in with his credentials, username should be saved as cookie value. Next time when user is going to login, he can just enter username and should be logged in successfully.
Could someone please help me out
Since security is a complex matter, I recommend using Spring Security, even though you're tasked to do it without. To illustrate the complexity about security, I can already tell you that your current code has a vulnerability, since you're trusting a plaintext username cookie as your sole authentication. Spring Security on the other hand uses a key to generate a remember me cookie so that it is much more difficult to impersonate someone (unless you know the key).
So, if you would be using Spring Security, the first thing you need to do is to create a UserDetailsService, which has a method called loadByUsername(). To implement this, you could use your UserService and use the User builder to construct a Spring Security user object:
public class MyUserDetailsService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if ("admin".equalsIgnoreCase(username)) {
return User.builder()
.username(username)
// This should contain the hashed password for the requested user
.password("$2a$10$T5viXrOTIkraRe2mZPyZH.MAqKaR6x38L.rbmRp53yQ8R/cFrJkda")
// If you don't need roles, just provide a default one, eg. "USER"
.roles("USER", "ADMIN")
.build();
} else {
// Throw this exception if the user was not found
throw new UsernameNotFoundException("User not found");
}
}
Be aware, in contrary to your original UserService.findByUsernameAndPassword() you do not have to check the password by yourself, just retrieve the user object and pass the hashed password.
The next step is to provide a proper PasswordEncoder bean. In my example I'm using BCrypt with 10 rotations, so I created the following bean:
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(10);
}
#Bean
public UserDetailsService userDetailsService() {
return new MyUserDetailsService();
}
}
The next step is to configure Spring Security. For example:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/**")
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html").permitAll()
.loginProcessingUrl("/login-user").permitAll().usernameParameter("username").passwordParameter("password")
.defaultSuccessUrl("/welcome.html")
.and()
.rememberMe()
.alwaysRemember(true)
.tokenValiditySeconds(30*5)
.rememberMeCookieName("mouni")
.key("somesecret")
.and()
.csrf().disable();
}
In this case, all endpoints (/**) will be secured, you'll have a login form at login.html containing two form fields (username and password). The destination of the form should be /login-user and when a user is successfully logged in, he will be redirected to /welcome.html.
Similar to what you wrote in your code, this will generate a cookie called mouni containing a value (no longer a plain username) and it will be valid for 150 seconds, just like in your example.
I'm disabling CSRF here because I'm using a simple HTML form, and otherwise I would have to add a templating engine to pass the CSRF key. Ideally, you should keep this enabled.
You are using Spring framework which has the capability for the same which you are trying to achieve. so why to do it manually?
Have a look at spring security.
https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/

Spring Security: authentication page permitAll seems to be ignored

I am writing an application using Spring Security.
I've implemented my custom UserDetails, UserDetailsService, AccessDecisionVoter and WebSecurityConfigurerAdapter.
I want to permit unauthorized users to access the /authenthication/login page to log in, but every other access to a page needs to be handled by the custom AccessDecisionVoter.
My custom WebSecurityConfigurerAdapter class looks like this:
#Configuration
#EnableWebSecurity
#ComponentScan(value = "security")
public class Configuration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsServiceImpl userDetailsServiceImpl;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/authentication/login**")
.permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.accessDecisionManager(accessDecisionManager())
;
}
#Bean
public AccessDecisionManager accessDecisionManager() {
List<AccessDecisionVoter<? extends Object>> decisionVoters =
Arrays.asList(new AccessDecisionVoterImpl());
return new UnanimousBased(decisionVoters);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) {
try {
auth.userDetailsService(userDetailsServiceImpl);
} catch (Exception e) {
e.printStackTrace();
}
}
#Bean
public UserDetailsServiceImpl userDetailsService() {
return userDetailsServiceImpl;
}
}
I have defined several roles in my database. The vote method in my custom AccessDecisionVoter retrieves the permissions of the User that is logged in and grants or denies access, based on that and the URL + httpMethod.
Problem:
However when I send a POST to the /authentication/login with the username and password my code gives me a NullPointerException in the custom AccessDecisionVoter: the username (retrieved via the authentication.getPrincipal(); returns anonymousUser, which results in the NullPointer later in the code. But I don't understand why the vote method gets called anyway since the configuration file told Spring to permittAll accesses to /authentication/login
I believe that the way you have it set up, permitAll just makes it so that all requests are allowed (even unauthenticated ones) but it doesn't disable the security chain for that request, it will still try to process through them all. If you just want to bypass all the security, the approach i usually use is to override the webSecurity config method and add the exception cases there, like so:
#Override
public void configure(WebSecurity webSecurity) throws Exception
{
webSecurity
.ignoring()
.antMatchers("/authentication/login**");
}

Spring webSecurity.ignoring() doesn't ignore custom filter

I have a set a custom authentication filter in my Spring 4 MVC + Security + Boot project. The filter does it's job well and now I want to disable the security for some URI (like /api/**). Here is my configuration:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity.ignoring().antMatchers("/api/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilterBefore(filter, BasicAuthenticationFilter.class);
}
}
Unfortunately, when I call a resource under /api/... the filter is still chained. I've added println in my filter and it's written to the console on every call. Do you know what's wrong with my configuration?
UPDATE
Filter code:
#Component
public class EAccessAuthenticationFilter extends RequestHeaderAuthenticationFilter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("FILTER");
if(SecurityContextHolder.getContext().getAuthentication() == null){
//Do my authentication stuff
PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(user, credential, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
super.doFilter(request, response, chain);
}
#Override
#Autowired
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
}
remove #Component on class EAccessAuthenticationFilter,and like this:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilterBefore(new EAccessAuthenticationFilter(), BasicAuthenticationFilter.class);
}
https://github.com/spring-projects/spring-security/issues/3958
I don't have enough reputation to add a comment, but for anyone like me who was looking for a little more of an explanation for kimhom's answer, WebSecurityConfigurerAdapter will tell Spring Security to ignore any filters added through it. The filter was then still being invoked because the #Component (or any flavor of #Bean) annotation told Spring to add the filter (again) outside of the security chain. So while the filter was being ignored in the security chain, it was not being ignored by the other (non-security?) chain.
This solved two weeks of headaches for me. In my case my custom filter needed the Authentication object given by the SecurityContext where it kept coming up as null because the security chain was never executed.
I had the correct configuration to ignore some context path in the web security configuration as below..
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v1/api1").antMatchers("/v1/api2");
}
But I mistakenly had added #PreAuthorize(...) on my controller method and it seems like that method level security was overriding any security configuration set up at the start.
After few tests I realized that in fact my configurations are ok and it's just a comprehension problem. The spring.security.ignored=/api/** doesn't bypass or turn off the filter. In reality every request still pass through my custom filter, but the difference is that Spring Security doesn't mind of the authentication status nor the granted authority coming from the custom filter.
I was wondering that the "ignored" property simply bypass the spring security filters. It sounds like I was totally wrong...
I always found the easiest way to do this is to put this configuration in your application.properties:
security.ignored=/api/**
I think you also need it in the Filter class as well (extends RequestHeaderAuthenticationFilter) i.e.
public class EAccessAuthenticationFilter extends RequestHeaderAuthenticationFilter {
public EAccessAuthenticationFilter() {
super(new RequestMatcher() {
RequestMatcher matcher = new AntPathRequestMatcher("/v1/api1");
return matcher.matches(request);
});
}
}

Spring security : handling SSO and DB users

I have two different types of users.
SSO users
DB users.
SSO users would have already been authenticated by different system and DB users should be authenticated by our system. Can i configure Spring security to handle this scenario where by i can say prompt login page for some users and don't prompt for some.
Lets say for SSO users i can get there users ID in request headers while DB when they access the application there is no user id present in request header .How can i handle this scenario ?
Can i override authenticate method of DaoAuthenticationProvider by extending it and then page on some parameter decide to authenticate user or is there any other means ? can i add any information to Authentication class
This is What i have tried to Far
Security Config java
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder builder) throws Exception {
builder.jdbcAuthentication().dataSource(dataSource).passwordEncoder(passwordEncoder())
.usersByUsernameQuery("select username,password, enabled from users where username=?")
.authoritiesByUsernameQuery("select username, role from user_roles where username=?");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().httpBasic()
.and().addFilterBefore(new UserTypeFilter(), BasicAuthenticationFilter.class);
}
public PasswordEncoder passwordEncoder() {
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
/*
* #Bean public MethodSecurityInterceptor methodSecurityService() { return
* new MethodSecurityInterceptor(); }
*/
#Bean(name="myAuthenticationManager")
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public ExceptionTranslationFilter exceptionTranslationFilter() {
ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(
new Http403ForbiddenEntryPoint());
AccessDeniedHandlerImpl accessDeniedHandlerImpl = new AccessDeniedHandlerImpl();
accessDeniedHandlerImpl.setErrorPage("/exception");
exceptionTranslationFilter
.setAccessDeniedHandler(accessDeniedHandlerImpl);
exceptionTranslationFilter.afterPropertiesSet();
return exceptionTranslationFilter;
}
#Bean
public UserTypeFilter authenticationFilter() throws Exception {
UserTypeFilter authFilter = new UserTypeFilter();
authFilter.setAuthenticationManager(authenticationManager());
return authFilter;
}
}
My Custom AbstractAuthenticationProcessingFilter
public class UserTypeFilter extends AbstractAuthenticationProcessingFilter {
private static final String INTERCEPTOR_PROCESS_URL = "/index";
#Autowired
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
public UserTypeFilter() {
super(INTERCEPTOR_PROCESS_URL);
}
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
System.out.println(" BASIC AUTHENTICATION FILTER");
String userId = request.getHeader("USERID");
if (userId == null) {
System.out.println(" THROWING EXCEPTION FILTER");
throw new PreAuthenticatedCredentialsNotFoundException("USERID param not found");
}
return null;
}
}
My Controller
#Controller
public class MainController {
#RequestMapping(value = { "/index" }, method = RequestMethod.GET)
public ModelAndView index() {
ModelAndView model = new ModelAndView();
model.addObject("message", "This is test page!");
model.setViewName("dummy");
return model;
}
}
The control goes to My Custom filter and then when the exception is thrown but ExceptionTranslationFilter is not getting called
Have i configured httpsecurity correctly
Have i configured My custom filter correctly
Have i configured ExceptionTranslation Filter
Am i missing anything
This is a pretty standard use case for spring security. You will need to provide an Authentication object into the security context before any security interceptor is encountered.
Typically you would have some kind of filter which extracted SSO parameters from the request, authenticated those parameters against the SSO service, and then create an Authentication object and put it into the security context. The type of filter and configuration of the filter will depend on what SSO technology you are using.
There would often also be a filter (usually an ExceptionTranslationFilter) which will send unauthenticated requests to a login page.
There would also be filters to receive the parameters from the login form and store them in the security context.
Putting it all together I would expect one possible workflow to be:
User logs in with SSO parameters
Request comes in prepopulated with credentials
Some filter extracts those credentials, verifies them, creates an Authentication object, places the object in the security context.
The security interceptor finds the Authentication object in the security context, verifies the user is allowed access to the particular function, and passes the request on.
User logs in without SSO parameters (needs login page)
Request comes in with no credentials
The security interceptor finds no Authentication object and throws an exception.
The ExceptionTranslationFilter turns the exception into a redirect to a login page.
User logs in with filled out login form (e.g. for DB login)
Request comes in with a login form as the entity body
Some filter (e.g. UsernamePasswordAuthenticationFilter) extracts the credentials from the login form and defers to an authentication provider (e.g. your DAO authentication provider) to query the database and verify the user. If verified this filter will create an Authentication object and place it in the security context.
The security interceptor finds the Authentication object in the security context, verifies the user is allowed access to the particular function, and passes the request on.

How to disable or override RequestCacheAwareFilter in Spring Boot

I have very basic simple Spring Boot Rest application.
I needed to implement custom authentication in Spring Security: for every REST request I need to check username and password, that are in specific headers of every request ("username" and "password").
So I implemented custom AuthEntryPoint:
#Service
public class CustomAuthEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
String username = httpServletRequest.getHeader("username");
String password = httpServletRequest.getHeader("password");
if (!username.equals("admin") || !password.equals("admin")) {
throw new RuntimeException("", new BadCredentialsException("Wrong password"));
}
}
}
So, I realized, that RequestCacheAwareFilter is caching first request and headers are also stored in cache. So if I make a request with wrong pass and then with right one, I will still get an exception.
So, how could I override the CacheAwareFilter or disable it? Or am I doing something totally wrong?
Use custom WebSecurityConfigurerAdapter to set request cache to NullRequestCache:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestCache()
.requestCache(new NullRequestCache());
}
}
I just made the app stateless like here: How can I use Spring Security without sessions?
And now everything is okay.

Categories

Resources