I am trying to implement a Spring security filter as follows:
#Configuration
#EnableWebSecurity
open class OpenApiConfigurer : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http.addFilter(object : FilterSecurityInterceptor() {
override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain?) {
super.doFilter(request, response, chain)
}
})
}
...
}
I can confirm the #Configuration is loaded because, the configure method is invoked and the filter is added. However the method doFilter is never invoked – I can call whichever requests, but it never does anything inside of it.
What might be wrong? Do I need to do something special?
The reason was following:
// Even though this class is not a bean/service/configuration, it must be defined for
// the Spring-Security to work - otherwise the filters are never invoked with no error.
open class SecurityInitializer : AbstractSecurityWebApplicationInitializer()
I had the same problem, which was fixed by adding an implementation of the AbstractSecurityWebApplicationInitializer (see: Why is Spring Security not working?).
Related
My web app is built with Spring MVC (4.2.9.RELEASE) and Spring Security (3.2.5.RELEASE). I use the loadUserByUsername method to find the user from the database.
public class MyUserDetailsServiceImpl implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
//how to get the HttpSession from within this methd?
}
....
}
How can I get the HttpSession from within this method?
In the applicationContext.xml file, I have the following for security:
<http create-session="always" use-expressions="true" request-matcher="regex" entry-point-ref="authenticationEntryPoint" >
....
</http>
There is a way to get current request:
Get the request object from RequestHolderContext and from that one get session:
public Optional<HttpServletRequest> getCurrentHttpRequest() {
return Optional.ofNullable(RequestContextHolder.getRequestAttributes())
.filter(requestAttributes -> ServletRequestAttributes.class.isAssignableFrom(requestAttributes.getClass()))
.map(requestAttributes -> ((ServletRequestAttributes) requestAttributes))
.map(ServletRequestAttributes::getRequest);
}
Use it like this:
Optional<HttpServletRequest> request = getCurrentHttpRequest();
HttpServletRequest currentRequest = request.get();
Hope this helps you
There is an answer exactly to your question at Spring Security documentation of more recent version (5.1.5 for instance). In short:
How do I access the HttpSession from a UserDetailsService?
You can’t, since the UserDetailsService has no awareness of the servlet API.
The #Atul's answer can be a good workaround but please be aware that RequestContextHolder internally relies on the current thread and thus may miss the session if called from another thread.
Before making my post, I already have a solution and it seems working, but not sure whether my solution is error-free or Spring already has a more elegant solution. Here is my solution:
I built filter as follows:
public class MyFilter implements Filter {
...
public void doFilter(ServletRequest request, ServletResponse response, FilterChain
chain) throws IOException, ServletException {
//create a thread-safe object and record the request object
}
}
Then, in the loadUserByUsername method, get the request object (thus the HTTP session) from the thread-safe object.
Comments? Please feel free let me know whether this approach has any problem.
UPDATE
MyFilter is declared in web.xml, together with Spring Security's DelegatingFilterProxy.
I'm trying to get OAuth2 based SSO working with spring. I've created a server and client, but I've run into an issue where after enabling the Authorization and Resource server.
#SpringBootApplication
#EnableAuthorizationServer
#EnableResourceServer
open class GsAuthenticationApplication {
companion object {
#JvmStatic fun main(args: Array<String>) {
SpringApplication.run(GsAuthenticationApplication::class.java, *args)
}
}
}
Once that's all wired up, the generated /login page is no longer available, as it seems to be treated as an protected OAuth resource
<oauth>
<error_description>
Full authentication is required to access this resource
</error_description>
<error>unauthorized</error>
</oauth>
I created a WebSecurityCOnfigurerAdapter bean, and tried to explicitly set it up with the configure method below, but it's still giving me the same issue
override fun configure(http: HttpSecurity?) {
//super.configure(http)
// #formatter:off
http
?.authorizeRequests()
?.and()
?.formLogin()
?.loginPage("/login")
?.permitAll()
// #formatter:on
}
We have an application which is using spring-security-oauth2:1.0. I was trying to change it to a newer version, spring-security-oauth2:2.0.7.RELEASE. Some classes were removed, some package structure is changed, I managed to sort out all those things and I was able to start the server without any issue. But I am facing a strange issue here.
With OAuth2 - 1.0 version, when the user logs in we used to do a GET request on /oauth/token, For example :
http://localhost:8080/echo/oauth/token?grant_type=password&client_id=ws&client_secret=secret&scope=read,write&username=john#abc.com&password=password123
and It used to work just fine.
When I try the same thing, First of all I am not able to make a GET request because of the logic in TokenEndPoint.java
private Set<HttpMethod> allowedRequestMethods = new HashSet<HttpMethod>(Arrays.asList(HttpMethod.POST));
#RequestMapping(value = "/oauth/token", method=RequestMethod.GET)
public ResponseEntity<OAuth2AccessToken> getAccessToken(Principal principal, #RequestParam
Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
if (!allowedRequestMethods.contains(HttpMethod.GET)) {
throw new HttpRequestMethodNotSupportedException("GET");
}
return postAccessToken(principal, parameters);
}
I have tried to make a POST request same as above URL, but I get InsufficientAuthenticationException with the error message
There is no client authentication. Try adding an appropriate authentication filter
This is because of the following POST request controller in TokenEndpoint.java. When I debug, I see that principal is null.
#RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, #RequestParam
Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
//principal is null here
if (!(principal instanceof Authentication)) {
throw new InsufficientAuthenticationException(
"There is no client authentication. Try adding an appropriate authentication filter.");
}
.............
}
I have an authentication filter and it worked well when I used version 1.0. This is the relevant prats of my config:
<authentication-manager xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="userDetailsService"/>
</authentication-manager>
<bean id="userDetailsService" class="com.hcl.nc.service.UserDetailsService">
<constructor-arg><ref bean="sessionFactory" /></constructor-arg>
</bean>
I always thought that the request will be authenticated by authentication-provider and goes to token-endpoint but that does not seem to be the correct flow. After debugging the application with version 2.0.7, now I really doubt my understanding about the flow.
Could somebody please explain why it worked in previous version and why it's not working now?
Do I have do to something different to get a OAuth token??
NOTE: I have already checked these questions : here, here, here. But I was not able to find the correct solution.
I don't know the previous version, but I know a bit about 2.0.7.
I suspect your problem is that your TokenEndpoint security tries to authenticate your clients against your user service.
The TokenEndpoint is protected by a BasicAuthenticationFilter. By default this filter would use an AuthenticationManager instance, which itself holds an AuthenticationProvider, which itself depends on an instance of UserDetailsService.
The trick is that this particular instance of UserDetailsService must be client based, not user based : that's why there is a ClientDetailsUserDetailsService, which adapts ClientDetailsService to UserDetailsService.
Normally all this stuff is already done by default when you use the framework's configuration classes AuthorizationServerConfigurerAdapter, #EnableAuthorizationServer, etc..
I had the same problem and my application.yml had this line:
servlet:
path: /auth
so the token address was: /auth/oauth/token
I remove the path from application.yml so the token path became:
/oauth/token
And everything works fine.
I hope this help
One of the problems of the following error, can be that authentication was not performed. I have encountered this problem with older implementation of Spring.
verify that:
TokenEndpoint -> postAccessToken method. Check if Principal is not null. If it is null it means that Basic Authroziation was not performed.
One of the solution to add filter was to use:
#Configuration
public class FilterChainInitializer extends AbstractSecurityWebApplicationInitializer {
}
More information about AbstractSecurityWebApplicationInitializer can be found in Spring docs
The problem can be because of opening all requests. You should remove it.
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/**");
}
in my case, i found this config:
security.allowFormAuthenticationForClients(); // here
then post this
http://localhost:8081/sso/oauth/token?client_id=unity-client&client_secret=unity&grant_type=authorization_code&code=Yk4Sum&redirect_uri=http://localhost:8082/sso-demo/passport/login
its works for me, try it
#Configuration
#EnableAuthorizationServer
public class Oauth2Config extends AuthorizationServerConfigurerAdapter {
private static final Logger log = LoggerFactory.getLogger(Oauth2Config.class);
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients(); // here
}
#Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception { // #formatter:off
clients.inMemory()
.withClient("unity-client")
.secret("unity")
.authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
.scopes("foo", "read", "write")
.accessTokenValiditySeconds(3600) // 1 hour
.refreshTokenValiditySeconds(2592000) // 30 days
;
} // #formatter:on
#Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
}
}
I am following this tutorial - Practical Guide to Building an API Back End with Spring Boot'. See https://www.infoq.com/minibooks/spring-boot-building-api-backend , But with the latest SpringBoot Version(2.7)
and I run into this problem:
org.springframework.security.authentication.InsufficientAuthenticationException: There is no client authentication. Try adding an appropriate authentication filter. at org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(TokenEndpoint.java:91) ~[spring-security-oauth2-2.3.5.RELEASE.jar:na]
My solution/fix was to annotate WebSecurityGlobalConfig with #EnableWebSecurity because in the original course this annotation was missing.
So adding this annotaiton has fixed the error for me.
I'm having some trouble testing a Spring Boot application with MockMvc.
I have the following test class:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {SpringConfiguration.class, SecurityConfiguration.class})
#IntegrationTest({"server.port=8080"})
#WebAppConfiguration
public class DemoTest {
#Autowired
private EmbeddedWebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void testGetAccountUnauthenticated() throws Exception {
mockMvc.perform(get("/accounts/1").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isUnauthorized());
}
}
This results in a HTTP 200 not a 401. I have component scanning and autoconfiguration enabled and spring security is configured in my SecuityConfiguration class as follows:
#Configuration
#EnableWebSecurity
#EnableWebMvcSecurity // required for use of #AuthenticationPrincipal in MVC controllers.
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) {
web.debug(true);
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//set up authentication.
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
// set up form login
}
}
If I use a RestTemplate to access http://localhost:8080/accounts/1 then I get the expected behaviour (HTTP 401).
I have seen other examples (e.g. Spring Boot setup security for testing) that suggest that I autowire the FilterChainProxy and add the filter manually using the WebApplicationContext.addFilters(filterChainProxy) method. However, this actually fails for me (org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.web.FilterChainProxy] found).
I have two questions:
Why does the injected WebApplicationContext not automatically use the SpringSecurity filters? Even if I could get the FilterChainProxy and add it manually, the JavaDoc for EmbeddedWebApplicationContext states
any {#link Servlet} or {#link Filter} beans defined in the context will be automatically registered with the embedded Servlet container
As a result I wouldn't expect to have to manually add the security filter chain since I (incorrectly?) expect this to "just work" due to the Auto Configuration magic in Spring Boot?
Why is there no FilterChainProxy in the application context? Again, perhaps my expectations of the AutoConfiguration is incorrect - but I thought that this would be configured as part of the context configuration.
Thanks in advance for any advice.
Edits
The reason a FilterChainProxy doesn't get injected was because I has my configuration set to
public void configure(WebSecurity web) {
web.debug(true);
}
This actually configures a org.springframework.security.web.debug.DebugFilter instead. The way I have now managed to get the Filter regardless of this debug setting is as follows:
#Resource(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
private Filter securityFilter;
If I add this to the MockMvcBuilder as follows:
MockMvcBuilders.webAppContextSetup(webApplicationContext).addFilters(securityFilter)
then it does work as expected.
But, I don't understand why MockMVC would ignore the filters as this seems important for testing a request since anything could happen in a Filter that might impact the outcome of the test. Furthermore, it means that to test properly I'd need to lookup all Filters in the servlet context and establish their priority/url mapping and add them appropriately. This seems error prone and unnecessary.
I agree that MockMVC is perhaps more for testing SpringMVC and custom code in controllers, as commented by #dave-syer. So in cases when one wants to test spring MVC infrastructure with your custom controller code at the same time (correctness of controllers mapped to URLs; mapping and validation of input and output objects; standard controllers; your controllers) without leveraging the Servlet container part of the stack, MockMVC is there for you.
But MockMVC also does have methods to add filters, so it is designed with a possibility to engage Filters in the described type of testing. Sometimes filter may play functional role for code inside of a controller and that would be otherwise not testable with MockMVC.
With all that theory in mind I was trying to mimic Boot behaviour for my tests where filters would be set up in Spring Boot way and picked up by my tests to be used with MockVMC. Here is a snippet that I ended up using. It can surely be enhanced to mimic Boot behaviour in more precisely and extracted to some custom MockMVCBuilder.
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setUp() {
Collection<Filter> filterCollection = wac.getBeansOfType(Filter.class).values();
Filter[] filters = filterCollection.toArray(new Filter[filterCollection.size()]);
mockMvc = MockMvcBuilders.webAppContextSetup(wac).addFilters(filters).build();
}
Have you tried this?
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
...
#Slf4j
#RunWith(SpringRunner.class)
#SpringBootTest
public class AuthorizeTest {
#Autowired
private WebApplicationContext wac;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders
.webAppContextSetup(wac)
.apply(springSecurity())
.build();
}
...
}
In my case it is 403, not 401, but you get the idea.
I use spring security core plugin. I want to put an object in session just after user has logged in. What I've discovered so far is that there is grails.plugin.springsecurity.LoginController in the plugin. And it has method which is called ajaxSuccess which seems to be invoked just after successfull authentication. So I decided to create another LoginController which extends default one and overrides this method:
#Secured('permitAll')
class LoginController extends grails.plugin.springsecurity.LoginController {
def ajaxSuccess() {
session['somevproperty'] = someValue
super.ajaxSuccess()
}
}
but debugging shows that this method is never invoked. What is going wrong? May there is another way to do what I want? Thank you!
Spring security has it own event listeners. I prefer you use that.
http://grails-plugins.github.io/grails-spring-security-core/guide/events.html
Sample code from above link for success login.
package com.foo.bar
import org.springframework.context.ApplicationListener
import org.springframework.security.authentication.event. AuthenticationSuccessEvent
class MySecurityEventListener implements ApplicationListener<AuthenticationSuccessEvent> {
void onApplicationEvent(AuthenticationSuccessEvent event) {
// handle the event
}
}