Apply HttpSecurity for GET request - spring

I have this rest endpoint:
#RestController
#RequestMapping("/data")
public class DataPagesController {
#GetMapping("/page")
public ResponseEntity<?> page() {
List<String> list = new ArrayList<>();
list.add("test 1");
list.add("test 2");
list.add("test 3");
list.add("test 4");
return new ResponseEntity<>(list, HttpStatus.OK);
}
}
I have configured context path prefix into the project using:
server:
port: 8080
servlet:
context-path: /engine
I want to restrict the access using:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.httpBasic()
// Allow GET for dashboard page
.authorizeRequests().antMatchers(HttpMethod.GET, "/data/page").authenticated()
// Allow all requests by logged in users
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
But I get error:
GET http://localhost:8080/engine/data/page 401
Do you know what is the proper way to configure endpoint permissions for GET request?
One possible idea:
Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
19:12:37.834 [http-nio-8080-exec-8] DEBUG AffirmativeBased[decide:66] - Voter: org.springframework.security.web.access.expression.WebExpressionVoter#659a59ae, returned: -1
19:12:37.834 [http-nio-8080-exec-8] DEBUG ExceptionTranslationFilter[handleSpringSecurityException:180] - Access is denied (user is anonymous); redirecting to authentication entry point
I need to remove maybe .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

401 means that you are not authorized, so it means you are not passing the token you have configured, or your are not passing as spring it is expecting, or the token you are passing is wrong / expired
Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided. The response must include a WWW-Authenticate header field containing a challenge applicable to the requested resource. See Basic access authentication and Digest access authentication.[32] 401 semantically means "unauthorised",[33] the user does not have valid authentication credentials for the target resource.
401 error code from wikipedia

Related

Endpoint that is locked down with hasIpAddress requires "Full authentication is required to access this resource" [duplicate]

This question already has an answer here:
can't get hasIpAddress working on Spring Security
(1 answer)
Closed 2 months ago.
I have two different means of security.
is locked down with an IP check, and only an IP check, I do not care about further authentication.
is a role check where I want to authenticate with a resource server and validate the token.
With the code I have, the role check is working as expected. I get a 200, 401, and 403 correctly. This issue is when I try to hit the endpoint that does the IP check, I get an error of
Full authentication is required to access this resourceunauthorized
I only want to perform an IP address check.
LOGS
Checking match of request : '/locked'; against '\/locked'
Secure object: FilterInvocation: URL: /locked; Attributes: [#oauth2.throwOnError(hasIpAddress('192.168.216.0/23') or hasIpAddress('10.4.7.59'))]
Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken#c36755eb: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
Voter: org.springframework.security.web.access.expression.WebExpressionVoter#40adb6a3, returned: -1
Access is denied (user is anonymous); redirecting to authentication entry point
Code
(Spring Boot 2.2.2)
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
#Configuration
#EnableResourceServer
#ConditionalOnProperty(prefix = "whitelist", value = "enabled", havingValue = "true", matchIfMissing = false)
public class EnabledSecurityConfig extends ResourceServerConfigurerAdapter {
private static final String ESIGN_PURGE_BATCH = "ESIGN_PURGE_BATCH";
#Override
public void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable();
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http
.csrf().disable();
http
.authorizeRequests()
.regexMatchers(HttpMethod.GET, "\\/locked").access("hasIpAddress('192.168.216.0/23') or hasIpAddress('10.4.7.59')")
.antMatchers(HttpMethod.POST, "/purgePackets").hasRole(ESIGN_PURGE_BATCH)
.anyRequest().permitAll();
}
}
implementation("org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.2.2.RELEASE")
then in my properties I set (But this works for the role check as mentioned)
security.oauth2.resource.jwk.key-set-uri=https://ourOauthserver.com/uaa/oauth/token_keys
The answer in my case was the wrong IP address for testing. You do not want to use the IP that the provider assigns, instead the local IP that I should be using, which was
0:0:0:0:0:0:0:1
Spring does NOT provide a log that says IPs failed to match, and so when it said "Full Authentication is needed" I just ASSUMED it was doing more than I wanted and getting passed the IP.

Spring Filter chain - antMatchers(...).authenticated() not being enforced

I have an application using a custom Jwt token implementation.
The authentication portion works just fine, with the token being created/validated just fine. My Security configuration looks like this:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
public class DJWTSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
JwtTokenProvider jwtTokenProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/*").authenticated()
.antMatchers("/api/auth/signin").permitAll()
.and()
.apply(new JwtConfigurer(jwtTokenProvider));
}
}
For some reason, security is not being enforced to api requests. For requests sent without bearer header, it seems that Spring considers "anonymous" users as being authenticated. Inspecting the security context:
org.springframework.security.authentication.AnonymousAuthenticationToken#f27f7551:
Principal: anonymousUser;
Credentials: [PROTECTED];
Authenticated: true;
Details: org.springframework.security.web.authentication.WebAuthenticationDetails#b364: RemoteIpAddress: 0:0:0:0:0:0:0:1;
SessionId: null;
Granted Authorities: ROLE_ANONYMOUS
Is this expected behavior that should be fixed by adding a role constraint to the route?
Wildcard was wrong - it should be /api/***
not relevant for the given error, but the order should be the other way around

Unable to stop Concurrent Session in Spring Boot (Keycloak Authentication Server)

I am able to implement authentication functionality using Spring Security and Keycloak(Working as expected). However, when I tried to stop concurrent login in the Spring Security configuration it is not working. I tried with keycloak configuration also ,however I came to know there is no such in build functionality/configuration to stop concurrent login.
Spring Boot Keycloak Configuration is as follows:
SessionRegistry sesionregistry=new SessionRegistryImpl();
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(sesionregistry);
}
#Bean
public KeycloakConfigResolver KeycloakConfigResolver() {return new KeycloakSpringBootConfigResolver();}
#Override
protected void configure(HttpSecurity http) throws Exception
{
super.configure(http);
http
.authorizeRequests()
.antMatchers("/login").hasRole("user")
.antMatchers("/app/*").hasRole("user")
//.antMatchers("/logout").permitAll()
.anyRequest().permitAll()
.and()
.authorizeRequests()
.antMatchers("/admin*").hasRole("admin")
.anyRequest().permitAll()
.and()
.logout().logoutUrl("/logout")
.and()
.sessionManagement().maximumSessions(1)
.maxSessionsPreventsLogin(true)
; //for maximum 1 session for any user
}
Using this configuration, I am able to login however concurrent login are not disabled.
I tried to print Principal Object for same user different login and found that:
Session 1:
org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken#ec24d1c4:Principal: c8827230-1916-4026-923c-642be90d0d38; Credentials: [PROTECTED]; Authenticated: true; Details: org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount#7f2460bd; Granted Authorities: ROLE_user, ROLE_uma_authorization
Session 2:
org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken#87138d52: Principal: c8827230-1916-4026-923c-642be90d0d38; Credentials: [PROTECTED]; Authenticated: true; Details: org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount#1e21cde6; Granted Authorities: ROLE_user, ROLE_uma_authorization
Even the hash value to 2 principal object differes:
Session 1: Printing Principal HAsh Value: -1854104148
Session 2: Printing Principal HAsh Value: -1904262684
I don't know how to solve the concurrent login issue. Any help appreciated.
Thanks

Spring Security: Secure endpoint based on client authority

I'm currently using Spring Security's OAuth2 to implement authorization across a number of micro services. Our AuthService performs all of the authentication with OAuth2 tokens etc and can create users.
Consider two clients: Client A and Client B.
Client A has authorities: CREATE_USER, CREATE_POST
Client B has authorities: READ_USER
(Yes we could use a scope instead, but this is just an example!)
Aim:
Only Client A, which has the authority CREATE_USER, should be allowed to create a user. Users are creating by posting to /users.
Problem:
The problem is that when I send a POST request to the /users endpoint with the basic authentication header for Client A, the CREATE_USER authority is not found because the request hit the AnonymousAuthenticationFilter and the only authority found is ROLE_ANONYMOUS and I receive the following:
10:38:34.852 [http-nio-9999-exec-1] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /users; Attributes: [#oauth2.throwOnError(#oauth2.hasAuthority('CREATE_USER))]
10:38:34.852 [http-nio-9999-exec-1] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken#9055c2bc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
10:38:34.854 [http-nio-9999-exec-1] DEBUG o.s.s.access.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter#a63e3e8, returned: -1
10:38:34.856 [http-nio-9999-exec-1] DEBUG o.s.s.w.a.ExceptionTranslationFilter - Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
One incredibly hacky solution would be to register a custom security filter that reads the basic auth header and verifies that a client's name is equal to Client A, but this will not work for a third client, Client C which also has the CREATE_VIEWER authority, as the name and not the authorities are verified here.
// UsersController.kt
#PostMapping("/users")
#ResponseStatus(HttpStatus.OK)
#ResponseBody
fun createUser(): String {
return "Created user!"
}
Client Configuration
override fun configure(clients: ClientDetailsServiceConfigurer?) {
clients!!.inMemory()
.withClient("ClientA")
.scopes("all")
.authorities("CREATE_USER", "CREATE_POST")
.authorizedGrantTypes("refresh_token", "password")
.and()
.withClient("ClientB")
.scopes("all")
.authorities("READ_USER")
.authorizedGrantTypes("refresh_token", "password")
}
WebSecurityConfigurerAdaptor impl
override fun configure(http: HttpSecurity) {
http.requestMatchers().antMatchers("/oauth/authorize", "/oauth/confirm_access")
.and()
.authorizeRequests()
.antMatchers("/users").access("hasAuthority('CREATE_USER')")
.anyRequest().authenticated()
.and()
.csrf().disable()
}
override fun configure(auth: AuthenticationManagerBuilder) {
auth.authenticationProvider(authenticationProvider())
}
#Bean
open fun authenticationProvider(): DaoAuthenticationProvider {
val authProvider = DaoAuthenticationProvider()
authProvider.setUserDetailsService(userCredentialService)
authProvider.setPasswordEncoder(passwordEncoderService)
return authProvider
}

Spring Boot Rest Spring Security Authentication Issue with CORS

I am developing a Spring REST application using Spring Boot. I am consuming the Spring REST APIs using angular JS client.
The stack I am using is :
Spring Boot 1.4.2 , Spring Security , Angular JS , Tomcat 8
My Issue Is :
1)At login , user gets succesfully authenticated. Custom authentication success handler returns 200 status code.
2)After authentication success , when clint sends another request to access a procted resource , HttpSession is NULL
3)Due to which when SecurityContextPersistenceFilter tries to retrieve the SecurityContext from HttpSession it is returned as null and
and again server sends request to /login resource
4)So my questions are :
1)Why is httpsesion is null for second request ?
2)I have observed that Spring security returns JSESSIONID as cookie after first succesfull authentication.
3)But after that , client is NOT sending this JSESSIONID in request hence second request won't be in same session.
4)If that is the case , how is SecurityContext will be retrieved if SESSION is not established ?
Please help as I am not able to proceed here
EDIT 1 :
I am using default in memory user provided by spring security. My security configuration is bellow :
#Configuration
#EnableWebSecurity
#ComponentScan({"com.atul.security"})
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
private RESTAuthenticationSuccessHandler authenticationSuccessHandler;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().successHandler(authenticationSuccessHandler)
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS,"/*").permitAll()
.antMatchers("/index.html", "/home.html", "/login.html", "/").permitAll()
.anyRequest().authenticated()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
I am getting below user as authenticated user :
org.springframework.security.authentication.AnonymousAuthenticationToken#9055c2bc:
Principal: anonymousUser; Credentials: [PROTECTED];
Authenticated: true;
Details: org.springframework.security.web.authentication.WebAuthenticationDetails#b364:
RemoteIpAddress: 0:0:0:0:0:0:0:1;
SessionId: null;
Granted Authorities: ROLE_ANONYMOUS
The request is now failing at : AbstractSecurityInterceptor : AccessDecisionVoter returning false for authenticated expression and access denied exception is thrown
There is no session in rest because RESTFUL services don't maintain state, read this stack overflow post:
Do sessions really violate RESTfulness?
If you want to know how properly build spring security service based REST examine this tutorial:
http://www.baeldung.com/securing-a-restful-web-service-with-spring-security
Hope that this helps.
You are using formLogin security, which means you need to add some kind of data store to authenticate users against it.
Here is an example for in-memory data store from spring documentation:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public UserDetailsService userDetailsService() throws Exception {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user").password("password").roles("USER").build());
manager.createUser(User.withUsername("admin").password("password").roles("USER","ADMIN").build());
return manager;
}
}
according to this example, you suppose to authenticate successfully with a username of user and password of password
You are trying to apply security with angularJS as your front technology, there's a great tutorial how to achieve that, I already implemented it on my project, here's the link:
https://spring.io/guides/tutorials/spring-security-and-angular-js/
For starter you need to use httpBasic and not formLogin as your security authentication method.
After banging head for a long time , I am finally able to find out the solution.
Steps are :
1) Form based login requires session to be established . When authentication succeeds , server sends Set-Cookie:JSESSIONID=3974317C6DE34865B6726FCFD4C98C08; Path=/springbootrest; HttpOnly header in response.
2)Browser has to send this header in every request else there will be no session established and authentication will fail
3)But since I was using CORS (my server and ui app reside on different port on local system) , browser was not sending Cookie:JSESSIONID=3974317C6DE34865B6726FCFD4C98C08 in request failing the authentication.
4)I have to add below line $httpProvider.defaults.withCredentials = true; in my angular js client after that browser started sending above header in request.
5)At server side I have to add below code in CORSFilter.
response.setHeader("Access-Control-Allow-Credentials","true");
response.setHeader("Access-Control-Allow-Origin", "http://localhost:8186");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, WWW-Authenticate, Authorization, Origin, Content-Type, Version");
response.setHeader("Access-Control-Expose-Headers", "X-Requested-With, WWW-Authenticate, Authorization, Origin, Content-Type");
final HttpServletRequest request = (HttpServletRequest) req;
if("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}

Resources