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>
Related
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.
I am trying to convert a Spring Security 3 #Secured("admin") annotation into Spring Security 4 compatible fashion.
This is my usersService.java
#PreAuthorize("hasAuthority('admin')")
public List<User> getAllUsers() {
return usersDao.getAllUsers();
}
Then in security-context.xml I have:
<security:intercept-url pattern="/admin" access="permitAll" />
...
<security:global-method-security pre-post-annotations="enabled" />
getAllUsers() is called by a LoginController.java
#RequestMapping("/admin")
public String showAdmin(Model model) {
List<User> users = usersService.getAllUsers();
model.addAttribute("users", users);
return "admin";
}
In mySql database, there are two tables, users and authorities. authorities has 2 columns, username and authority. administrator has authority admin.
Now if I trie to access /admin, I will be redirected to /login, but after I log in with administrator, I still get "access denied".
I think I must have missed something very basic but as I am new to Spring, I could not figure it out. Any help would be appreciated. Thanks.
Update: I tried changing the annotation to #PreAuthorize("hasRole('ROLE_ADMIN')") and I also changed the "authority" column in mySql for admin from "admin" to "ROLE_ADMIN" but it still gives me 403. I did not have much faith on this because before this error, I had to change hasRole('admin') in securityContext.xml to hasAuthority('admin').
Although it's late, nevertheless
hasRole(...) set a prefix for the the content - the default one is ROLE_
hasAuthority(...) checks the content WITHOUT a prefix, i.e. just the pure content
You should add in Spring security
#EnableGlobalMethodSecurity(prePostEnabled = true)
Try this #PreAuthorize("hasRole('ROLE_ADMIN')")
When I have spring security settings as below:
<http pattern="/ui/**" auto-config="true" authentication-manager-ref="authenticationManager">
<intercept-url pattern="/ui/login*" access="isAnonymous() or hasRole('USER')" requires-channel="${web.channel}" />
<intercept-url pattern="/ui/**" access="hasRole('USER')" requires-channel="${web.channel}" />
<form-login login-page="/ui/login" login-processing-url="/ui/j_spring_security_check" default-target-url="/ui/dashboard" />
<logout logout-url="/ui/logout" logout-success-url="/ui/login" />
</http>
my controller is ignored and both methods are never invoked. I've tried adding breakpoints inside both of them but the execution never stops. I have also tried adding System.out.println() to them and nothing was written to system output, so I am pretty sure both methods are either not mapped or just never invoked.
#Controller
public class MyController {
#RequestMapping(value = { "/ui", "/ui/login"}, method = GET)
public String indexNoTrailingSlash() {
return index();
}
#RequestMapping(value = "/", method = GET)
public String index() {
return "redirect:/ui/dashboard";
}
}
Security works fine and thanks to default-target-url="/ui/dashboard" in the xml I do get to my dashboard page after successful login. What I want to achieve is, however, that a user is redirected to dashboard also if he's already logged in. I've tried to follow this answer and that's what I need the controller for. Is there a way either to map these methods so I can use my controller for redirecting or to somehow redirect with just spring security?
Thanks :)
If the answer link you included is what you look for, do the following for your case.
return new ModelAndView("redirect:/");
It will redirect to your index() method.
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.
I have a web application built using Java Spring MVC.
I'm just setting up spring security connecting to an LDAP server for authentication.
I've successfully set it up so that I am able to login to my application but I can't find anything to help me in mapping an AD group to a user role within Java as I can only get a 403 forbidden page i.e. I've been authenticated but don't have permissions yet.
I currently have:
<http auto-config="true">
<intercept-url pattern="/**" access="ROLE_USER" />
</http>
<ldap-server id="ldapServer" url="LDAPURL" manager-dn="USER" manager-password="PASSWORD" />
<authentication-manager >
<ldap-authentication-provider
group-search-base="OU=GROUPS"
group-search-filter="sAMAccountName={0}"
user-search-base="OU=USERS"
user-search-filter="sAMAccountName={0}"
/>
</authentication-manager>
Say that user was a part of the AD group g-group-UK-user I then want to be able to map that AD group to ROLE_USER so that user can then see the whole web app.
I can only seem to find very simple examples where the groups are either ADMIN or USER in which case the prefix ROLE is just added to the group or the other method seems to be using UserDetailContextMapper but I can't find a clear use of this.
To do this I used the following within authentication manager:
user-context-mapper-ref="customUserContextMapper"
I then used the following class to check if that user belongs to a certain AD group and then assign the ROLE_USER role to their authorities:
#Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities)
{
Attributes attributes = ctx.getAttributes();
Object[] groups = new Object[100];
groups = ctx.getObjectAttributes("memberOf");
LOGGER.debug("Attributes: {}", attributes);
Set<GrantedAuthority> authority = new HashSet<GrantedAuthority>();
for(Object group: groups)
{
if (group.toString().toLowerCase().contains("AD_GROUP_NAME".toLowerCase()) == true)
{
authority.add(new SimpleGrantedAuthority("ROLE_USER"));
break;
}
}
User userDetails = new User(username, "", false, false, false, false, authority);
return userDetails;
}
Please note that the class is a little more complicated than usual because of the LDAP server I was connecting which has a different structure than usual in that the groups a user has access to are stored in an attribute under the user and not the other way round in which a group would have as an attribute all the users that belong to it.