How exactly works this Spring Security example? - java

I am pretty new in Spring Security and I have some doubt related the configuration found into a tutorial.
This is the spring-security.xml file used to the Spring Security configuration into the project:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<security:http>
<security:intercept-url pattern="/springLogin" access="permitAll"/>
<security:intercept-url pattern="/doSpringLogin" access="permitAll"/>
<security:intercept-url pattern="/myprofile" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/springHome" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/products" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/springLogout" access="permitAll"/>
<security:intercept-url pattern="/springLogin?error=true" access="permitAll"/>
<security:form-login login-page="/springLogin" login-processing-url="/doSpringLogin"
default-target-url="/springHome" authentication-failure-url="/springLogin?error=true"
username-parameter="username" password-parameter="password"
/>
<security:csrf disabled="true"/>
<security:logout logout-url="/springLogout" logout-success-url="/springLogin"/>
</security:http>
<bean id="userDetailsServiceImpl" class="com.demo.security.UserDetailsServiceImpl"></bean>
<bean id="authenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsServiceImpl"></property>
</bean>
<bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
<constructor-arg name="providers">
<list>
<ref bean="authenticationProvider"/>
</list>
</constructor-arg>
</bean>
<security:authentication-manager>
<security:authentication-provider user-service-ref="userDetailsServiceImpl">
<security:password-encoder hash="plaintext"></security:password-encoder>
</security:authentication-provider>
</security:authentication-manager>
</beans>
I it divided into some section. The first one is the tag content.
It contains something as:
<security:intercept-url pattern="/springLogin" access="permitAll"/>
that I think means that the page related to the /springLogin resource is accessible to everyone while
<security:intercept-url pattern="/myprofile" access="hasRole('ROLE_USER')"/>
means that the resource related to the /myprofile resource is accessible only to the logged user (the principal) having a ROLE_USER role setted.
Is it this reasoning correct?
Then in the previous configuration file there is:
1) The declaration of the authenticationManager bean:
<bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
<constructor-arg name="providers">
<list>
<ref bean="authenticationProvider"/>
</list>
</constructor-arg>
</bean>
that I think it is used by Spring to populate the SecurityContext with the Principal objects (for example all the user of a web application) and with the Authorities (what a specific Principal can do).
Is this reasoning correct?
This object take as constructor arg a list of autentication provider bean that have to provide the Principal informations (so for example the role associated to a specific Principal)
In this case is provided an implementation of the DaoAuthenticationProvider class that take a bean having name="userDetailsService" as property, this one:
<bean id="userDetailsServiceImpl" class="com.demo.security.UserDetailsServiceImpl"></bean>
that is an instance of the UserDetailsServiceImpl class, this one:
public class UserDetailsServiceImpl implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
System.out.println(username);
User user = RegisteryDAO.getUserDAO().getUserByUsername(username);
if(user == null){
return null;
}
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority(user.getRole()));
UserDetails userDetails = new org.springframework.security.core.userdetails.
User(user.getUsername(), user.getPassword(), true, true, true, true, authorities);
return userDetails;
}
}
So what exactly happen?
Using the debugger it seems to me that when te user try to access to a specific page this loadUserByUsername() return the UserDetails object related to the logged user that contain the List representing the roles associated to the specific logged user (for example the previous ROLE_USER)
Then I think that Spring automatically use the
<security:intercept-url pattern="/myprofile" access="hasRole('ROLE_USER')"/>
to check if the user have setted the propper role into the previous List list.
If it have so forward the request to the controller method that handle this Http Request otherwise avoid that this HttpRequest come to this controller method and show a page that say that the user can't access to this resource.

Here is an explanation of some of the concepts and questions you are asking about.
AuthenticationManager
AuthenticationManager is the component responsible for processing the Authentication request. The Authentication request might be instance of UsernamePasswordAuthenticationToken for username/password logins.
For other implementations look at Authentication JavaDoc.
AuthenticationManager also has collection of AuthenticationProvider implementations. These components are capable of processing specific Authentication types and AuthenticationManager iterates through them attempting to find one capable of handling the Authentication passed to it. If it finds one, it calls it with Authentication object presented and returns fully populated Authentication object if successful (otherwise AuthenticationException is thrown).
AuthenticationProvider
As mentioned above, AuthenticationProvider processes certain type of Authentication request. For instance the DaoAuthenticationProvider will perform following steps when called by AuthenticationManager:
Take the UsernamePasswordAuthenticationToken passed to it
Use the UserDetailsService service implementation provided to it (in your case it is UserDetailServiceImpl) to look up user by username
Check the password provided in the authentication token against the user using PasswordEncoder and SaltSource, if specified.
If the authentication succeeds, returns the populated Authentication object (UsernamePasswordAuthenticationToken in this case), which contains principal, credentials and is marked as authenticated
In case the authentication fails, AuthenticationException will be thrown
DaoAuthenticationProvider which you are using which is capable of processing UsernamePasswordAuthenticationToken requests. So typically form logins, and so on. You can see which types of authentications provider support by looking at its supports() method implementation, which in case of DaoAuthenticationProvider looks like this:
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
Spring Security Filter Chain
Now let's look at the Security filter chain, as defined by Spring Security documentation:
The order that filters are defined in the chain is very important.
Irrespective of which filters you are actually using, the order should
be as follows:
ChannelProcessingFilter, because it might need to redirect to a different protocol
SecurityContextPersistenceFilter, so a SecurityContext can be set up in the SecurityContextHolder at the beginning of a web request, and
any changes to the SecurityContext can be copied to the HttpSession
when the web request ends (ready for use with the next web request)
ConcurrentSessionFilter, because it uses the SecurityContextHolder functionality but needs to update the SessionRegistry to reflect
ongoing requests from the principal
Authentication processing mechanisms - UsernamePasswordAuthenticationFilter, CasAuthenticationFilter,
BasicAuthenticationFilter etc - so that the SecurityContextHolder can
be modified to contain a valid Authentication request token
The SecurityContextHolderAwareRequestFilter, if you are using it to install a Spring Security aware HttpServletRequestWrapper into your
servlet container
RememberMeAuthenticationFilter, so that if no earlier authentication processing mechanism updated the SecurityContextHolder,
and the request presents a cookie that enables remember-me services to
take place, a suitable remembered Authentication object will be put
there
AnonymousAuthenticationFilter, so that if no earlier authentication processing mechanism updated the SecurityContextHolder,
an anonymous Authentication object will be put there
ExceptionTranslationFilter, to catch any Spring Security exceptions so that either an HTTP error response can be returned or an
appropriate AuthenticationEntryPoint can be launched
FilterSecurityInterceptor, to protect web URIs and raise exceptions when access is denied
When the user submits login form, AuthenticationManager is called at step 4 in the filter chain. In the case of form login it would be handled by UsernamePasswordAuthenticationFilter which calls the AuthenticationManager to process the authentication:
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
// ...
return this.getAuthenticationManager().authenticate(authRequest);
}
Using the debugger it seems to me that when te user try to access to a specific page this loadUserByUsername() return the UserDetails
Actually the loadUserByUsername() is called when user authenticates, for instance after submitting login form. If the user is already authenticated this is not called.
I think that means that the page related to the /springLogin resource is accessible to everyone:
<security:intercept-url pattern="/springLogin" access="permitAll" />
Then I think that Spring will automatically use following to check if the user has proper role:
<security:intercept-url pattern="/myprofile" access="hasRole('ROLE_USER')" />
Correct. This process is handled by FilterSecurityInterceptor, which extends AbstractSecurityInterceptor - core Spring Security component dealing with authorization. If the user is not authenticated or doesn't have the required role an Exception is thrown and handled by the ExceptionTranslationFilter. This filter handles Security exceptions. For instance in case of authentication failure it will redirect user to authentication entry point, e.g. the login page.
Internal architecture of Spring Security is pretty nicely described in the reference documentation. I recommend to take a look at it.

Related

Spring security OpenID flow using JWT for authentication

I am trying to do authentication for my web application using widely used OpenId Connect flow.
I understand that the user who wants to authenticate on example1.com will first be redirected to the authentication server (or OP : "OpenId Provider"). The user authenticates on that server which then redirects him back to the original example1.com site with a signed JWT token.
Technical challenges:
I am suing spring security as part of my web application. I took a custom filter to redirect the user to authentication server and then it redirects back to my example1.com with valid JWT (Json Web Token).
My Custom Filter:
public class MyCustomFilter extends AbstractAuthenticationProcessingFilter {
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
//reading the AWT access token from reqest beahder
String token = request.getHeader("Authorization");
if(token == null){
// redirecting user to authentication server
String accessCodeUrl = "authentication.com?query string"
response.sendRedirect(accessCodeUrl);
} else{
response.addHeader("Authorization", token);
}
//Now how can i inject the above received token to spring security
}
}
My spring xml config:
<security:http auto-config="false" entry-point-ref="entryPoint" disable-url-rewriting="true">
<!-- set disabled to true to remove Cross Site Request Forger support -->
<security:csrf disabled="true" />
<security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER','ROLE_ADMIN')" />
<security:custom-filter ref="${MyCustomFilter }" position="PRE_AUTH_FILTER" />
<security:access-denied-handler error-page="${security.accessDeniedPage}"/>
<security:headers>
<security:frame-options policy="SAMEORIGIN"/>
</security:headers>
</security:http>
My custom filter is extending AbstractAuthenticationProcessingFilter. is it good fit for my requirement or should i consider extending any other filter's like BasicAuthenticationFilter or OncePerRequestFilter or GenericFilterBean.
I am confused here as to take 1 or 2 filters. because first time i am redirecting to authorization server, but from second time i should use the JWT token provided by authentication server.
Since i am going with stateless approach. I need to exchange JWT token every time between example.com and browser. I am using ExtJs as front end, what is the best practice to exchange JWT token between browser and example.com.
As per my understanding i should send JWT token every time from browser along with every request. Does that mean should i manually inject every time JWT token as part of Ajax request or Do we have some approach to append to all the outgoing Ajax requests.

Does Spring Security go well with custom authentication and authorization code?

I'm working on a REST service provider web application with Java + Spring and I wonder if I can fuse my in-house authentication and authorization code with Spring Security?
To be more specific, I have two methods (that I like to keep):
User authenticate(String username, String password) { ... }
boolean authorize(User user, String resource) { ... }
In this scenario, a REST API is a resource and the second method verifies whether it can be called by the user or not.
My question is if I switch to Spring Security then should I give in to it and annotate my methods with user names and role names!? Or is there a way that I can keep my way of thinking (that REST APIs are resources and annotating them with user names and role names is like hard-coding ACL)?
I know my question is somewhat vague but I've been struggling with it for some time now and I'll be really grateful if someone can give a straight answer.
You can inject your own authentication manager with password encoder.
Below is a post where I posted some questions about it a while ago. It's not an exact answer for you but it will lead you down the right track.
debug spring security authentication-manager
At the time I did it xml style but I'm sure you can change this to a java config style.
It definitely worked foe me!
Spring security also very useful for providing authentication and authorization to the REST URLs. We no need to specify any custom implementations.
First, you need to specify the entry-point-ref to restAuthenticationEntryPoint in your security configuration as below.
<security:http pattern="/api/**" entry-point-ref="restAuthenticationEntryPoint" use-expressions="true" auto-config="true" create-session="stateless" >
<security:intercept-url pattern="/api/userList" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/api/managerList" access="hasRole('ROLE_ADMIN')"/>
<security:custom-filter ref="preAuthFilter" position="PRE_AUTH_FILTER"/>
</security:http>
Implementation for the restAuthenticationEntryPoint might be as below.
#Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException ) throws IOException {
response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized" );
}
}
After this you need to specify RequestHeaderAuthenticationFilter. It contains the RequestHeader key. This is basically used for identifying the user`s authentication. Generally RequestHeader carries this information while making the REST calls.
For example consider below code
<bean id="preAuthFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<property name="principalRequestHeader" value="Authorization"/>
<property name="authenticationManager" ref="authenticationManager" />
</bean>
Here,
<property name="principalRequestHeader" value="Authorization"/>
"Authorization" is the the key presented the incoming request. It holds the required user`s authentication information.
Also you need to configure the PreAuthenticatedAuthenticationProvider to fulfill our requirement.
<bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
<bean id="userDetailsServiceWrapper"
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<property name="userDetailsService" ref="authenticationService"/>
</bean>
</property>
</bean>
This code will work for securing the REST urls by means of Authentication and authorization without any custom implementations.
My suggestion is go with Spring-AOP for custom authentication and authorization, if you want custom implementation for authentication and authorization which works similar to the spring security.
For Complete code please find the below link:
https://github.com/srinivas1918/spring-rest-security
I hope that, i understood your question.

SwitchUserFilter not working in Spring security when used with Basic Authentication

I am having a problem with SwitchUserFilter in Spring security. I have following configuration:
<bean id="ldapUserSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg name="searchBase" value=""/>
<constructor-arg name="searchFilter" value="(uid={0})"/>
<constructor-arg name="contextSource" ref="ldapContext"/>
</bean>
<security:ldap-server id="ldapContext" url="ldap://xxxxxxx"/>
<bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<constructor-arg name="authenticator">
<bean
class="org.springframework.security.ldap.authentication.BindAuthenticator">
<constructor-arg ref="ldapContext" />
<property name="userSearch" ref="ldapUserSearch" />
</bean>
</constructor-arg>
<constructor-arg name="authoritiesPopulator" ref="dbLDAPAuthPopulator" />
</bean>
<security:authentication-manager>
<security:authentication-provider ref="ldapAuthProvider"/>
</security:authentication-manager>
And the corresponding SwitchUserFilter bean is created as:
SwitchUserFilter switchUserFilter = new SwitchUserFilter();
switchUserFilter.setUserDetailsService(ldapUserDetailsService);
switchUserFilter.setTargetUrl("/");
switchUserFilter.setSwitchUserUrl("/impersonate");
switchUserFilter.setUsernameParameter("username");
switchUserFilter.setExitUserUrl("/unimpersonate");
When I go to the url "/impersonate" the user gets impersonated properly. However when the redirect is send to the target url i.e. "/" the user is again authenticated using basic auth.
I had a look at the code of both the SwitchUserFilter and BasicAuthenticationFilter and seems that SU will not work with basic auth.
This is what happens:
When the /impersonate?username=xyz url is called it goes to SwitchUserFilter which gets the details of xyz user from the ldap and it then sets the securitycontext in the session. Code snippet is as follows:
if (requiresSwitchUser(request)) {
// if set, attempt switch and store original
try {
Authentication targetUser = attemptSwitchUser(request);
// update the current context to the new target user
SecurityContextHolder.getContext().setAuthentication(targetUser);
// redirect to target url
successHandler.onAuthenticationSuccess(request, response, targetUser);
} catch (AuthenticationException e) {
logger.debug("Switch User failed", e);
failureHandler.onAuthenticationFailure(request, response, e);
}
return;
So in the SecurityContext you have information about xyz user.
Then when it redirects to target url i.e. "/" basicAuthenticationFilter is called which checks whether the user is authenticated. Code snippet:
Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
if(existingAuth == null || !existingAuth.isAuthenticated()) {
return true;
}
// Limit username comparison to providers which use usernames (ie UsernamePasswordAuthenticationToken)
// (see SEC-348)
if (existingAuth instanceof UsernamePasswordAuthenticationToken && !existingAuth.getName().equals(username)) {
return true;
}
// Handle unusual condition where an AnonymousAuthenticationToken is already present
// This shouldn't happen very often, as BasicProcessingFitler is meant to be earlier in the filter
// chain than AnonymousAuthenticationFilter. Nevertheless, presence of both an AnonymousAuthenticationToken
// together with a BASIC authentication request header should indicate reauthentication using the
// BASIC protocol is desirable. This behaviour is also consistent with that provided by form and digest,
// both of which force re-authentication if the respective header is detected (and in doing so replace
// any existing AnonymousAuthenticationToken). See SEC-610.
if (existingAuth instanceof AnonymousAuthenticationToken) {
return true;
}
return false;
As you can see it checks existingAuth.getName().equals(username)) which in this case it is xyz. However logged in user is different so the filter again authenticates the user and all the work done by SwitchUserFilter is overridden.
Is their any way to solve this issue? Can I override the BasicAuthenticationFilter?
This question is quite old, however should anyone come across it the answer is still valid today. You don't show your <http /> stanzas for Spring Security but you need to ensure that the role granted by impersonation is the same role (authority) required to bypass authentication at /*. If it's not then yes, you will be asked to authenticate.
You can specify custom authorities to be granted on impersonation by implementing an extension of SwitchUserAuthorityChanger and passing a reference of it to SwitchUserFilter.

Not able to access Spring Security Logged in User info

I am using Spring Security for authentication in my web-application.
Now, i need access to the User Information, for which the following method exists in the Controller:
#RequestMapping(value = "/userstuff")
#Controller
public class SomeUserController {
#RequestMapping(value = "getUser", method = RequestMethod.GET)
#ResponseBody
public UserDetails getUser(Locale locale, Model model) {
UserDetails userDetails = null;
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
if (authentication != null) {
Object principal = authentication.getPrincipal();
userDetails = (UserDetails) (principal instanceof UserDetails ? principal
: null);
}
return userDetails;
}
}
IF the controller url is put out of security check like this in applicationContext-Security.xml:
<security:http pattern="/userstuff/**" security="none" />
Then invoking
http:// host:port /app/userstuff/getUser - returns null.
BUT, if do commented it out (allowing spring security to intercept it):
<!-- <security:http pattern="/userstuff/**" security="none" /> -->
Then invoking:
http:// host:port /app/userstuff/getUser returns logged in user correctly.
Why so?
When you use security="none" all spring security filters will be switched off (see this entry from the official documentation). So no SecurityContextHolder, no #PreAuthorize, no other Spring Security features are available. For example normally SecurityContextHolder is populated by one filter from security filter chain.
As a workaround almost always you can replace security="none" construction by restriction for anonymous user only:
<security:http ... />
...
<security:intercept-url pattern="/userstuff/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
....
</security:http>

#PreAuthorize on spring controller sending redirect if authorization fails

I've got spring security successfully evaluating a #PreAuthorize on my controller. If i use "permitAll" then I can view the page, and if I use "isAuthenticated()" then I get an ugly Access is Denied stack trace. If I put the configuration in an intercept-url within the http node in my security context configuration xml file then I am nicely redirected to the login page instead of getting the nasty stack trace right in my page.
Is there a way for me to get the redirection with the annotation mechanism only?
I got this to work. There were a couple of things I had to deal with.
First, my Spring MVC configuration had a SimpleMappingExceptionResolver with a defaultErrorView configured. That was intercepting the Authentication and Authorization errors before they could get to the access-denied-handler that I had configured in the http element in my security configuration. The final code looks something like this.
securitycontext.xml
<global-method-security pre-post-annotations="enabled"/>
<!-- HTTP security configurations -->
<http auto-config="false" use-expressions="true" entry-point-ref="loginUrlAuthenticationEntryPoint">
<access-denied-handler ref="myAccessDeniedHandler" />
... other configuration here ...
</http>
<!-- handler for authorization failure. Will redirect to the login page. -->
<beans:bean id="myAccessDeniedHandler" class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<beans:property name="errorPage" value="/index" />
</beans:bean>
note that the loginUrlAuthenticationEntryPoint is actually not a part of the solution, it's the access-denied-handler.
my mvc-config.xml still has the SimpleMappingExceptionResolver, but with no defaultErrorView configured. If I were to continue with this path, I would probably implement my own SimpleMappingExceptionResolver that would let Authentication and Authorization excpetions pass through, OR configure it in the SimpleMappingExceptionResolver instead.
The killer in this deal is that I haven't found a way to configure the requires-channel="https" from the intercept-url through an annotation, so I'll be putting it in the xml configuration file for now anyway.
You can customize your error handling by overriding the security entry point. All exceptions that happen in the filter chain (defined by the mapping you define in the web.xml file) for secure objects get caught and can be handled there. If they are not handled then the ExceptionTranslationFilter will take over.
You can define your own entry point like this:
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, org.springframework.security.core.AuthenticationException authException) throws IOException, ServletException {
if (authException != null) {
// you can check for the spefic exception here and redirect like this
response.sendRedirect("403.html");
}
}
}
You can specify this as your entry point by setting this as you entry point in the xml config file:
<http entry-point-ref="customAuthenticationEntryPoint">
...
</http>
In your specific case the PreInvocationAuthorizationAdviceVoter will be invoked by whatever AccessDecisionManager you specify in your configuration (typically it is one of the following: AffirmativeBased, ConsensusBased, or UnanimousBased). You will see that these Voters throw the AccessDeniedException which you can specifically catch and handle in your entry point.
Grant

Categories

Resources