How can authenticate user by only ip address in spring security? - java

I have table with users. There is "ip_address" column in this table.
I have server - java server
I have web app on spring boot.
Spring boot communicates with java server by rest.
I need realize authonticate user in system by ip.
Whem user open web page - spring boot app get remoteIpAddress(String ipAddress = request.getRemoteAddr();) and pass it to java server in url. java server ckeck this ip in db in table users and if user can login return this user to spring boot app.
I want realize this with spring security. But when i open web page, in browser opened dialog window for input login and password. But I do not need login and password. I need if user is not null - give access to pages and save the user.
and if I input login and password and press "ok" button I move to my IPAddressBasedAuthenticationProvider
#Component
public class IPAddressBasedAuthenticationProvider implements AuthenticationProvider {
#Autowired
private HttpServletRequest request;
#Autowired
AuthService authService;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String ipAddress = request.getRemoteAddr();
AuthLkUser authLkUserByIp = authService.getAuthLkUserByIp(ipAddress);
if (authLkUserByIp == null) return null;
boolean b = authService.checkAuthLkUser(authLkUserByIp);
if (b) return null;
final List<GrantedAuthority> grantedAuths = new ArrayList<>();
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_ADMIN");
grantedAuths.add(grantedAuthority);
UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(authentication.getName(), authentication.getCredentials(), grantedAuths);
result.setDetails(authentication.getDetails());
return result;
}
#Override
public boolean supports(Class<?> aClass) {
return true;
}
}
And in this class saved user to session. And after that when I open web pages i not need input login and password. it contains in session. but my wep page not openned with error
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Mon Jul 11 14:27:59 ALMT 2016
There was an unexpected error (type=Forbidden, status=403).
Access is denied

For Spring Boot applications that run with the embedded Apache Tomcat container, you can use the org.apache.catalina.filters.RemoteAddrFilter from Apache Tomcat.
#Bean
public FilterRegistrationBean remoteAddressFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
RemoteAddrFilter filter = new RemoteAddrFilter();
// filter.setAllow("127.0.0.1");
filter.setAllow("127\\.0\\.0\\.1");
filterRegistrationBean.setFilter(filter);
filterRegistrationBean.addUrlPatterns("/gs/serving-web-content/testParameters");
return filterRegistrationBean;
}
allow : A regular expression (using java.util.regex) that the remote
client's IP address is compared to. If this attribute is specified,
the remote address MUST match for this request to be accepted. If this
attribute is not specified, all requests will be accepted UNLESS the
remote address matches a deny pattern.
You can refer this article - How do I restrict access to my application by IP address?

You can use Expression-Based Access Control provided by Spring. To secure individual URL, you are able to do something like this:
<http use-expressions="true">
<intercept-url pattern="/admin*"
access="hasRole('admin') and hasIpAddress('192.168.1.0/24')"/>
...
</http>
Where you created expression like if he has role admin and his IP address is xxx.xxx.x.x, then it is allowed. So you are looking for hasIpAddress.
More about expression-based access control is in their docs.
If you want to create this restrictions for Spring Boot and if you are using Apache Tomcat, then you are able to to enable a Servlet Filter (here is description) and then you can just enable Remote Address Filter and then set addresses which you want to allow. More abouy this filter is in Apache Tomcat docs.
And another one option is that if you will use Spring Security. There is class WebAuthenticationDetails which provide method getRemoteAddress().

Related

How to authenticate via session in the Spring Security from Angular?

So I have an Angular frontend application, and a Spring backend. Currently, it seems that I have a cookie of JSessionId on my application (which I receive only on login, and not on register, for whatever reason)
(cookies)
I assume it sends those cookies back to the server. (though that's only an assumption)
Now, when I am making a request to the protected server, the only thing I get is this "Please login" popup.
Login popup
When I log in, my UserService logs a user with such details:
UsernamePasswordAuthenticationToken [Principal=User(userId=1, name=Maksym Riabov, username=MRiabov, password={bcrypt}$2a$10$W0XJRQdfxV5XXORkr2bTluIHvFetIVBzmVp51l39T5zLCQk12RV1i, company=null, enabled=true), Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[ADMIN]]
And what I've noticed is that the sessionId is null there. Why could that be?
To answer some of the questions forward:
Yes, I've pasted {withCredentials: true} to every request. (specific to Angular)
Yes, I've read documentation - I've even tried pasting all the code from it and it seems that it didn't work.
My login controller:
#GetMapping("/login")
public ResponseEntity<String> login() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return ResponseEntity.ok("123123");
}
#PostMapping("/register")
public ResponseEntity<Map<String, String>> register(#RequestBody UserRegisterDto userDto) {
//todo check if name taken
User user = userMapper.toEntity2(userDto);
user.setPassword(passwordEncoder.encode(user.getPassword()));
user.setEnabled(true);
//todo remove
Authority authority = authorityRepository.save(new Authority("ADMIN"));
user.setAuthorities(Set.of(authority));
//todo REMOVE!!!!
User savedUser = userRepository.save(user);
System.out.println("registration works!");
return ResponseEntity.ok(Map.of("result",authority.getAuthority().getAuthority()));
}
Now, I am sending a request to the backend (which puts the popup above) like this one:
#PreAuthorize("hasRole('ADMIN')")
#GetMapping("/create")
public ResponseEntity<OnboardingPathDto> createOnboardingPath() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
// erased a bit of code here
return ResponseEntity.ok().build();
And as you see I have a method security, which throws the request for auth.
And, the cherry at the top:
#Component
#EnableWebSecurity
#EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
#RequiredArgsConstructor
public class SecurityConfig {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http, UserDetailsService userDetailsService) throws Exception {
http
.csrf().disable().cors().disable()
.authorizeHttpRequests()
.anyRequest().permitAll() //todo this is unsafe
.and().sessionManagement(session -> session.
sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.maximumSessions(1))//to force only one session per user
//here I tried sessionManagement to do something, but did it do something?
.rememberMe((rememberMe) -> rememberMe.userDetailsService(userDetailsService))
.httpBasic();
return http.build();
}
#Bean
public AuthenticationManager authenticationManager(DaoAuthenticationProvider daoAuthenticationProvider) throws Exception {
return new ProviderManager(daoAuthenticationProvider);
}
#Bean
public DaoAuthenticationProvider prov(PasswordEncoder passwordEncoder, UserDetailsService userDetailService) throws Exception {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
daoAuthenticationProvider.setUserDetailsService(userDetailService);
return daoAuthenticationProvider;
}
#Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {//to force only one session per user
return new HttpSessionEventPublisher();
}
I've read through the Spring Security documentation far and wide, and even have taken a course in it, but I still couldn't manage to get it working.
So, what I'm struggling with:
Why can't Spring authenticate through the session even though it is configured to do so? Where is my error?
Edit: I assume that sending the session directly into Angular (in REST, not in cookie) is really unsafe, right? I currently rely on cookies.
Edit 2: ffs, I'm sick of it, I'm just going to do oauth2 authentication.
Edit: I assume that sending the session directly into Angular (in REST, not in cookie) is really unsafe, right? I currently rely on cookies.
You are right, this is a bad idea. For sessions in an application running in a browser, only use cookies with those two flag raised (value=true):
secure (exchanged only over https)
http-only (hidden from Javascript).
This means that cookies should not be accessible to the Angular code but automatically set by the browser before sending requests to the backend.
You should also implement CSRF protection (which is the default in spring-security).
Edit 2: ffs, I'm sick of it, I'm just going to do oauth2 authentication.
Good idea. This is much better for security, user experience (SSO), and developper experience: most OIDC providers, either on premise (like Keycloak), or in the cloud (like Auth0, Cognito, and many others), already provide with login forms (including Multi Factor Authentication), user registration, profile edition, administration screens (like clients declaration, user roles assignement, etc.). For that:
configure your Spring REST API as a resource-server. I have written tutorials for this there
configure your Angular app either as:
an OAuth2 client. My favorite certified lib for Angular is angular-auth-oidc-client
a BFF client. Backend For Frontend is a pattern where a server-side middleware serves as the only OAuth2 client to hide tokens from the browser. Angular app won't be OAuth2: it will be secured with sessions (haha! your devil is back ;-), the middleware (something like spring-cloud-gateway with tokenRelay filter) will keep this session, associate tokens to it and replace sessions with tokens before forwarding requests to resource-server. Tutorial there.

Basics of Spring Security

What are the very basics of Spring Security i.e. how Spring sets up security internally. What are all the beans involved that are to be provided for Spring Security to work out-of-the-box?
I shall start first by explaining, how to bring in Spring Security into your application.
Just add below dependency to your application. Now, when you run your application the spring security is implemented by default. (As of April 2021, version might change in future)
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>5.4.5</version>
</dependency>
Closely looking at the console, you will see a password generated for default user: user. The password is a hash that you need to use.
When you access any URL from your application now, you will be restricted from Postman. From your browser, you will see a login page where you need to enter this username and password and you will be through to your URL. That sets up the inbuilt Spring Security.
But what is happening under the hood?
I shall answer it by reminding you of Servlets and Filters and DispatcherServlet in Spring.
DispatcherServlet is the very basic of Spring MVC and it forwards the requests to your controllers. Basically, DispatcherServlet is also a servlet.
I can create a chain of filters before DispatcherServlet and check my request for Authentication and Authorization before forwarding the request to hit my DispatcherServlet and then my controllers. This way, I can bring in Security to my application. This is exactly what the Spring Security does.
The below link very delicately highlights all the filters that are there before DispatcherServlet and what is the importance of those Filters. Please refer the link below:
How Spring Security Filter Chain works
Now, we need to understand what authentication and authorization is:
Authentication- Anyone using your application needs to have some info and you need to verify that user’s username, password to allow him to access your application. If his username or password is wrong, that means he is not authenticated.
Authorization- Once the user is authenticated, there might be some URLs of your application that should only be allowed to admin users and not normal users. This is called authorizing a user to access some parts of your application based on his role.
Let us look at some important Spring’s Filter in Filter Chain:
• BasicAuthenticationFilter: Tries to find a Basic Auth HTTP Header on the request and if found, tries to authenticate the user with the header’s username and password.
• UsernamePasswordAuthenticationFilter: Tries to find a username/password request parameter/POST body and if found, tries to authenticate the user with those values.
• DefaultLoginPageGeneratingFilter: Generates a login page for you, if you don’t explicitly disable that feature. THIS filter is why you get a default login page when enabling Spring Security.
• DefaultLogoutPageGeneratingFilter: Generates a logout page for you, if you don’t explicitly disable that feature.
• FilterSecurityInterceptor: Does your authorization.
These filters, by default, are providing you a login page which you saw on your browser. Also, they provide a logout page, ability to login with Basic Auth or Form Logins, as well as protecting against CSRF attacks.
Remember, the login page at the beginning just after adding Spring Security to your pom.xml. That is happening because of the below class:
public abstract class WebSecurityConfigurerAdapter implements
WebSecurityConfigurer<WebSecurity> {
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.httpBasic();
}
}
This WebSecurityConfigurerAdapter class is what we extend and we override its configure method. As per above, all the requests need to do basic authentication via form login method. This login page is the default provided by Spring that we saw when we accessed our URL.
Now, next question arises, what if we want to do this configuration ourselves? The below topic discusses exactly that:
How to configure Spring Security?
To configure Spring Security, we need to have a #Configuration, #EnableWebSecurity class which extends WebSecurityConfigurerAdapter class.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll()
.and()
.httpBasic();
}
}
You must do above the mentioned configurations. Now, you can do your specific security configuration i.e. which all URLs are allowed, which need to be authenticated, what are the types of authentication the application will perform and what are the roles that are allowed on specific URLs.
So, basically, all your authentication and authorization information is configured here. Other configuration regarding CORS, CSRF and other exploits is also done here, but that is out of the scope of the basics.
In the example above, all requests going to / and /home are allowed to any user i.e. anyone can access them and get response but the other requests need to be authenticated. Also, we have allowed form login i.e. when any request apart from / and /home is accessed, the user will be presented with a login page where he will input his username and password and that username/password will be authenticated using basic authentication i.e. sending in an HTTP Basic Auth Header to authenticate.
Till now, we have added Spring Security, protected our URLs, configured Spring Security. But, how will we check the username and password to be authenticated? The below discusses this:
You need to specify some #Beans to get Spring Security working. Why some beans are needed?
Because Spring Container needs these beans to implement security under the hood.
You need to provide these two beans – UserDetailsService & PasswordEncoder.
UserDetailsService – This is responsible for providing your user to the Spring container. The user can be present either in your DB, memory, anywhere. Ex: It can be stored in User table with username, password, roles and other columns.
#Bean
public UserDetailsService userDetailsService() {
return new MyUserDetailsService();
}
Above, we are providing our custom MyUserDetailsService which has to be a UserDetailsService child for Spring container to identify its purpose. Below is the sample implementation:
public class MyDatabaseUserDetailsService implements UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// Load the user from the users table by username. If not found, throw UsernameNotFoundException.
// Convert/wrap the user to a UserDetails object and return it.
return someUserDetails;
}
}
public interface UserDetails extends Serializable {
String getUsername();
String getPassword();
// isAccountNonExpired,isAccountNonLocked,
// isCredentialsNonExpired,isEnabled
}
You see, UserDetailsService shall provide the container with UserDetails object.
By default, Spring provides these implementations of UserDetailsService:
1. JdbcUserDetailsManager- which is a JDBC based UserDetailsService. You can configure it to match your user table/column structure.
2. InMemoryUserDetailsManager- which keeps all userdetails in memory. This is generally used for testing purposes.
3. org.springframework.security.core.userdetail.User– This is what is used mostly in custom applications. You can extend this User class on your custom implementation for your user object.
Now, as per above if any request arrives and needs to be authenticated, then since we have UserDetailsService in place, we will get the user from the UserDetails object returned by UserDetailsService for the user who has sent the request and can authenticate his sent username/password with the one received from our UserDetailsService.
This way, the user is authenticated.
Note: The password received from user is automatically hashed. So, if we do not have the hash representation of password from our UserDetailsService, it will fail even when the password is correct.
To prevent this, we provide PasswordEncoder bean to our container which will apply the hashing algorithm specified by the PasswordEncoder on the password in UserDetails object and make a hash for it. Then, it checks both the hashed passwords and authenticates or fails a user.
PasswordEncoder- This provides a hash of your password for security purposes. Why? You cannot/should not deal with plain passwords. That beats the very purpose of Spring Security. Better, hash it with any algorithm.
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
Now, you can autowire this PasswordEncoder anywhere in your application.
AuthenticationProvider-
In some cases, we do not have access to the user’s password but some other third party stores our user's information in some fancy way.
In those cases, we need to provide AuthenticationProvider beans to our Spring container. Once container has this object, it will try to authenticate with the implementation we have provided to authenticate with that third party which will give us a UserDetails object or any other object from which we can obtain our UserDetails object.
Once, this is obtained, that means we are authenticated and we will send back a UsernamePasswordAuthenticationToken with our username, password and authorities/roles. If it is not obtained, we can throw an exception.
#Bean
public AuthenticationProvider authenticationProvider() {
return new MyAuthenticationProvider();
}
An AuthenticationProvider consists primarily of one method and a basic implementation could look like this:
public class MyAuthenticationProvider implements AuthenticationProvider {
Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String username = authentication.getPrincipal().toString();
String password = authentication.getCredentials().toString();
User user = callThirdPartyService(username, password);
if (user == null) {
throw new AuthenticationException("Incorrect username/password");
}
return new UserNamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), user.getAuthorities());
}
}
Thats all there is to Spring Security basics or under the hood functionality and how we can leverage these to customize our security implementation. You can find examples anywhere. More advanced topics such as JWT, Oauth2 implementation, CSRF prevention, CORS allowance are beyond the scope.

Spring security - Authentication filters

I was wondering how can i get spring authentication during SSO.
A brief on the problem:
User logins into my app -> Spring custom filter (custom authentication provider for login with SSO) kicks in
Now that we have an authenticated user (bob/bob for example), we're trying to forward the request to a 3rd party application which has its own SSO - problem here is that it has its own in memory authentication (admin/admin for example)
now the 3rd party app prompts for auth (admin/admin) and the user bob/bob is now lost. Now when bob tries to log into my app he is not recognized as the session now has admin/admin.
How do i solve this problem, as the session information is being overridden by 3rd party Application ?
SecurityConfig.class
This would be the security config of ThirdParty App, which gets created when spring context is created
#Configuration
#Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManagerBuilder b = new InMemoryUserDetailsManagerBuilder()
.withUser("admin").password("admin").authorities(new String[]{"ROLE_ADMIN"});
return new CompositeUserDetailsService(Arrays.asList(new UserDetailsManager[]{b.build(), this.technicalUserDetailsService()}));
}
MyCustomConfig.class
This would be my custom config for security (SSO) also this gets created during spring context.
#configuration
#Bean
public UserDetailsService userDetailService() {
return new customUserDetailsService(); // Custom SSO
}
SSO is not working, as the fact that my custom config is having different authorities (user/roles) than the third party app.
any help is appreciated...

Unable to get user from kerberos for ldap authentication interceptor

I am using kerberos for authentication and ldap for authorization.
My kerberos authentication is working fine. Problem I am facing is ldap is called before kerberos returns username.
My ldap code is inside an interceptor, which is called first but it gets request.getRemotUser() as null and kerberos returns username after this as I can see in logs.
So how to call kerberos first and then ldap?
My ldap code is inside springs HandlerInterceptorAdaptor's preHandlerMethod inside Interceptor class :
#override
public boolean preHandler(HttpServletRequest request,HttpServletResponse response,Object handler){
String userName = request.getRemoteUser();
...
if (isAuthorized(userName)) {
//setting userName in session here
}
else {
//ldap authentication failed
}
}
Interceptor is defined in application-servlet.xml and kerberos is configured in security-config.xml , and both entries are made in web.xml. security-config.xml is before application-servlet.xml in web.xml
So in above code I get username as null but in logs when kerberos is called it returns me username. Sorry for not posting detailed code. I hope this question is understandable.
Maybe I do not quite understand the question / the problem your trying to solve, but why not inject a LdapUserDetailsServiceImpl, as the UserDetailsService of your KerberosServiceAuthenticationProvider?
That way Kerberos is doing the authentication and Ldap the authorization (i.e. populating the LdapUserDetails object).

How to manually set an authenticated user in Spring Security / SpringMVC

After a new user submits a 'New account' form, I want to manually log that user in so they don't have to login on the subsequent page.
The normal form login page going through the spring security interceptor works just fine.
In the new-account-form controller I am creating a UsernamePasswordAuthenticationToken and setting it in the SecurityContext manually:
SecurityContextHolder.getContext().setAuthentication(authentication);
On that same page I later check that the user is logged in with:
SecurityContextHolder.getContext().getAuthentication().getAuthorities();
This returns the authorities I set earlier in the authentication. All is well.
But when this same code is called on the very next page I load, the authentication token is just UserAnonymous.
I'm not clear why it did not keep the authentication I set on the previous request. Any thoughts?
Could it have to do with session ID's not being set up correctly?
Is there something that is possibly overwriting my authentication somehow?
Perhaps I just need another step to save the authentication?
Or is there something I need to do to declare the authentication across the whole session rather than a single request somehow?
Just looking for some thoughts that might help me see what's happening here.
I couldn't find any other full solutions so I thought I would post mine. This may be a bit of a hack, but it resolved the issue to the above problem:
#Autowired
AuthenticationServiceImpl authenticationManager;
public void login(HttpServletRequest request, String userName, String password) {
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(userName, password);
// Authenticate the user
Authentication authentication = authenticationManager.authenticate(authRequest);
SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.setAuthentication(authentication);
// Create a new session and add the security context.
HttpSession session = request.getSession(true);
session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
}
I had the same problem as you a while back. I can't remember the details but the following code got things working for me. This code is used within a Spring Webflow flow, hence the RequestContext and ExternalContext classes. But the part that is most relevant to you is the doAutoLogin method.
public String registerUser(UserRegistrationFormBean userRegistrationFormBean,
RequestContext requestContext,
ExternalContext externalContext) {
try {
Locale userLocale = requestContext.getExternalContext().getLocale();
this.userService.createNewUser(userRegistrationFormBean, userLocale, Constants.SYSTEM_USER_ID);
String emailAddress = userRegistrationFormBean.getChooseEmailAddressFormBean().getEmailAddress();
String password = userRegistrationFormBean.getChoosePasswordFormBean().getPassword();
doAutoLogin(emailAddress, password, (HttpServletRequest) externalContext.getNativeRequest());
return "success";
} catch (EmailAddressNotUniqueException e) {
MessageResolver messageResolvable
= new MessageBuilder().error()
.source(UserRegistrationFormBean.PROPERTYNAME_EMAIL_ADDRESS)
.code("userRegistration.emailAddress.not.unique")
.build();
requestContext.getMessageContext().addMessage(messageResolvable);
return "error";
}
}
private void doAutoLogin(String username, String password, HttpServletRequest request) {
try {
// Must be called from request filtered by Spring Security, otherwise SecurityContextHolder is not updated
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
token.setDetails(new WebAuthenticationDetails(request));
Authentication authentication = this.authenticationProvider.authenticate(token);
logger.debug("Logging in with [{}]", authentication.getPrincipal());
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (Exception e) {
SecurityContextHolder.getContext().setAuthentication(null);
logger.error("Failure in autoLogin", e);
}
}
Ultimately figured out the root of the problem.
When I create the security context manually no session object is created. Only when the request finishes processing does the Spring Security mechanism realize that the session object is null (when it tries to store the security context to the session after the request has been processed).
At the end of the request Spring Security creates a new session object and session ID. However this new session ID never makes it to the browser because it occurs at the end of the request, after the response to the browser has been made. This causes the new session ID (and hence the Security context containing my manually logged on user) to be lost when the next request contains the previous session ID.
Turn on debug logging to get a better picture of what is going on.
You can tell if the session cookies are being set by using a browser-side debugger to look at the headers returned in HTTP responses. (There are other ways too.)
One possibility is that SpringSecurity is setting secure session cookies, and your next page requested has an "http" URL instead of an "https" URL. (The browser won't send a secure cookie for an "http" URL.)
The new filtering feature in Servlet 2.4 basically alleviates the restriction that filters can only operate in the request flow before and after the actual request processing by the application server. Instead, Servlet 2.4 filters can now interact with the request dispatcher at every dispatch point. This means that when a Web resource forwards a request to another resource (for instance, a servlet forwarding the request to a JSP page in the same application), a filter can be operating before the request is handled by the targeted resource. It also means that should a Web resource include the output or function from other Web resources (for instance, a JSP page including the output from multiple other JSP pages), Servlet 2.4 filters can work before and after each of the included resources. .
To turn on that feature you need:
web.xml
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/<strike>*</strike></url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
RegistrationController
return "forward:/login?j_username=" + registrationModel.getUserEmail()
+ "&j_password=" + registrationModel.getPassword();
I was trying to test an extjs application and after sucessfully setting a testingAuthenticationToken this suddenly stopped working with no obvious cause.
I couldn't get the above answers to work so my solution was to skip out this bit of spring in the test environment. I introduced a seam around spring like this:
public class SpringUserAccessor implements UserAccessor
{
#Override
public User getUser()
{
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
return (User) authentication.getPrincipal();
}
}
User is a custom type here.
I'm then wrapping it in a class which just has an option for the test code to switch spring out.
public class CurrentUserAccessor
{
private static UserAccessor _accessor;
public CurrentUserAccessor()
{
_accessor = new SpringUserAccessor();
}
public User getUser()
{
return _accessor.getUser();
}
public static void UseTestingAccessor(User user)
{
_accessor = new TestUserAccessor(user);
}
}
The test version just looks like this:
public class TestUserAccessor implements UserAccessor
{
private static User _user;
public TestUserAccessor(User user)
{
_user = user;
}
#Override
public User getUser()
{
return _user;
}
}
In the calling code I'm still using a proper user loaded from the database:
User user = (User) _userService.loadUserByUsername(username);
CurrentUserAccessor.UseTestingAccessor(user);
Obviously this wont be suitable if you actually need to use the security but I'm running with a no-security setup for the testing deployment. I thought someone else might run into a similar situation. This is a pattern I've used for mocking out static dependencies before. The other alternative is you can maintain the staticness of the wrapper class but I prefer this one as the dependencies of the code are more explicit since you have to pass CurrentUserAccessor into classes where it is required.

Categories

Resources