Spring Security With Custom Login Flow - authentication

I am trying to add spring with spring security in an existing Struts based application. It is a Partial migration. All the new development should happen in Spring.
So what I want is,
User access a Secured URL
If the user is not authenticated Spring Should redirect to a specific URL (This will be a Struts URL)
After Old Struts Module does its work of authenticating the user, It saves an Object in HTTP Session which depicts the authenticated User.
Spring Should get this Object from Session and Authenticated the User.
Redirect the User to the requested Secured URL
I have tried to used formlogin().loginpage(<Struts_URL>). Also in the Struts code after authentication, I have updated the Spring's SecurityContext.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity pHttp) throws Exception
{
pHttp.antMatcher("/**").authorizeRequests()
.anyRequest().authenticated()
.and().formLogin().loginPage("/<Struts_Login_Entry>.do");
}
...
#Override
public void configure(WebSecurity pWeb) throws Exception
{
// All the Strtus URL has .do suffix so excluding it
pWeb.ignoring().antMatchers("/*.do", "/portal/**");
}
}
After Authentication in Struts
...
MyUserDetails lUserDetails = new MyUserDetails(pUserName, pRole);
Authentication auth = new UsernamePasswordAuthenticationToken(lUserDetails, null, lUserDetails.getAuthorities());
SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.setAuthentication(auth);
...
The User is getting redirected to Struts page when he accesses any Spring URL But this happens even if the user is authenticated.
I am not so familiar with Spring Security, any suggestion or help?

Related

I am receiving status 401 Unauthorized when I attempt to call my secured API by keycloak

I have an web application providing an REST API endpoints, secured with spring security and SSO Keycloak.
My web application works fine with protection on and I can access REST API endpoints using web browser, after authentication on keycloak and redirect back to endpoint. I am getting expected JSON response, which mean user and role should be configured correctly.
But when i want to call a provided REST API endpoint from another web application or using Postman, i have every time an error 401 error: unauthorized.
In the header of the request I am putting:
"Authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgO...." token.
I am able to fetch this token from keycloak in consumer web application and also when I am making a request using postman.
My Keycloak configuration in my spring security config:
#KeycloakConfiguration
public class KeycloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
/**
* Registers the KeycloakAuthenticationProvider with the authentication manager.
*/
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
/**
* Defines the session authentication strategy.
*/
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.authorizeRequests()
.antMatchers("/api/v1/**").hasRole("USER")
.antMatchers("/admin/**", "/app/**").hasRole("ADMIN")
.anyRequest().permitAll();
}
}
My keycloak.json file:
{
"realm": "realm-name",
"auth-server-url": "https://auth.server.com/auth/",
"ssl-required": "external",
"resource": "resource-name",
"verify-token-audience": true,
"credentials": {
"secret": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
},
"use-resource-role-mappings": true,
"confidential-port": 0
}
My method in the controller of the api:
#RestController
#RequestMapping("/api/v1")
public class WakeMeUpController {
#RequestMapping(value = "/test", method = RequestMethod.GET, produces = MediaType.TEXT_PLAIN_VALUE)
public String testMeUp() {
return "I am tested!";
}
}
This is a part of my client configuration in keycloak:
keycloak Client screen
Rest keycloak config is how it is coming by default.
Thx for your help!
Thank you. The problem was into audience configuration in keycloak.
For a client, you need to configure new Mapping under Client -> <client_name> -> Mappers and add another Mapping with mapper type 'Audience' and appropriate 'Included Client audience' (i choose client id i have used to make request)
Also another problem that i have faced after i solve this one, was that user was authenticated but was forbidden the access to endpoint.
Explicitly adding role into client configuration itself and assigning this role to the user solve it and it is working.
From huge help was setting spring logging to DEBUG
logging.level.org.springframework.security=DEBUG
And also XXX-Authentication header in server response that gives better description what the issue is.

url was not normalized error when using intellij but not when using STS

The developed website works fine on remote server and local machine (when using STS IDE) , recently I started use Intellij IDEA (I created a duplicate of the website code with no any changes ), I started getting the URL was not normalized error.
Does intellij handles Spring security somehow differently than STS ? or what could be the cause?
I don't want use custom httpfirewal .
#EnableGlobalMethodSecurity(prePostEnabled=true)
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider())
.jdbcAuthentication()
.usersByUsernameQuery(usersQuery)
.authoritiesByUsernameQuery(rolesQuery)
.dataSource(dataSource);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// URLs matching for access rights
http.authorizeRequests()
.antMatchers( "/", "/contact","/register").permitAll()
.antMatchers("/accounts").hasAnyAuthority("SUPER_USER","ADMIN_USER")
.anyRequest().authenticated()
.and()
// form login
.csrf().disable().formLogin()
.loginPage("/index")
.failureUrl("/index?error=true")
.defaultSuccessUrl("/user")
.usernameParameter("email")
.passwordParameter("password")
.and()
// logout
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/").and()
.exceptionHandling()
.accessDeniedPage("/access-denied");
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**");
}
and this is from the properties :
# Spring MVC view prefix.
spring.mvc.view.prefix=/templates/
# Spring MVC view suffix.
spring.mvc.view.suffix=.html
the error is :
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL was not normalized.
P.S: I'm using JDK8 ,Spring Boot 2,Spring Security ,thymeleaf,intellij U 2019.2
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL was not normalized.
Which IDE to use should not have any differences for running the same source codes on the embeddable server configured by springboot. This error happens when the HTTP requests that send to server is not normalised which the URL contains character sequences like ./, /../ , // or /. So I doubt that it is due to you are using different URL to browse the app. For example, you are accidentally adding a '/' in the URL such as http://127.0.0.1:8080/app//index.html
You can change to use a less secure HttpFirewall to avoid such checking by :
#Override
public void configure(WebSecurity web) throws Exception {
web.httpFirewall(new DefaultHttpFirewall());
//another configuration .....
}
P.S. Though it is called DefaultHttpFirewall , it is not the default HttpFirewall used by Spring Security since 4.2.4 which is less secured than the actual default StrictHttpFirewall

Is it ok to add access_token authorities to the OAuth2LoginAuthenticationToken?

I have a simple spring boot application with two services - ui and resource.
I trying to configure oauth2+oidc authentication using uaa server.
When I login in the ui service, spring security creates authentication result (in OidcAuthorizationCodeAuthenticationProvider) using id_token and it doesn't contain any scopes except openid. When the authentication result is created it contains only one authority - ROLE_USER so a can't use authorization on the client side.
Is is ok to override OidcUserService and add to the user's authorities scopes from the access_token to check access on the client side?
#Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
OidcUser user = super.loadUser(userRequest);
Collection<? extends GrantedAuthority> authorities = buildAuthorities(
user,
userRequest.getAccessToken().getScopes()
);
return new DefaultOidcUser(
authorities,
userRequest.getIdToken(),
user.getUserInfo()
);
}
Security configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.mvcMatchers("/protected/**").hasAuthority("SCOPE_protected")
.anyRequest().authenticated()
.and()
.oauth2Login()
.userInfoEndpoint().oidcUserService(oidcUserService())
.and()
...
It works but I'm not sure it's a good idea.
It is the approach as outlined in the Spring Security documentation, so the approach is fine.
The only thing is that when I have implemented it, I didn't add all the scopes to the authorities set - I pulled out the specific claim that had the role information - a custom groups claim that I configured in the identity provider's authorization server.
I include some example code for how to do this with Spring Webflux as most examples show how to do it with Spring MVC as per your code.
note: I'm very inexperienced with using reactor!
public class CustomClaimsOidcReactiveOAuth2UserService implements ReactiveOAuth2UserService<OidcUserRequest, OidcUser> {
private final OidcReactiveOAuth2UserService service = new OidcReactiveOAuth2UserService();
public Mono<OidcUser> loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
log.debug("inside CustomClaimsOidcReactiveOAuth2UserService..");
Mono<OidcUser> mOidcUser = service.loadUser(userRequest);
return mOidcUser
.log()
.cast(DefaultOidcUser.class)
.map(DefaultOidcUser::getClaims)
.flatMapIterable(Map::entrySet)
.filter(entry -> entry.getKey().equals("groups"))
.flatMapIterable(roleEntry -> (JSONArray) roleEntry.getValue())
.map(roleString -> {
log.debug("roleString={}", roleString);
return new OidcUserAuthority((String) roleString, userRequest.getIdToken(), null);
})
.collect(Collectors.toSet())
.map(authorities -> {
log.debug("authorities={}", authorities);
return new DefaultOidcUser(authorities, userRequest.getIdToken());
});
}
}
...
#Bean
ReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService() {
return new CustomClaimsOidcReactiveOAuth2UserService();
}

How to get past the Authentication Required Spring-boot Security

I have put in the password which is "root" and it keeps popping back up. How can I suppress this or get rid of it. I am using spring boot and spring security.
application.properties
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springbootpractice
spring.datasource.username=root
spring.jpa.database = MYSQL
spring.jpa.show-sql = true
# Hibernate
hibernate.dialect: org.hibernate.dialect.MySQL5Dialect
hibernate.show_sql: true
hibernate.hbm2ddl.auto: update
entitymanager.packagesToScan: /
I am using intellij 14 if that matters.
----Update 1-----
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/index").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/index")
.permitAll()
.and()
.logout()
.permitAll();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/index").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/index")
.permitAll()
.and()
.logout()
.permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
This class has to be in a parent package of all other packages:
WebSecurityConfig.
Also in application.properties set:
security.basic.enabled=false
ACV's answer is probably the easiest way to turn off the authentication completely by adding security.basic.enabled=false to the application.properties file which is usually located under src/main/resources folder.
or you just type in the password :)
1. use default password
When you run your spring application, there is usually a whole bunch of logging printed, which people usually don't read. The password is actually generated and printed to the screen at the startup. and the username is simply user. If you are testing using a browser and it probably only need you enter it once and caches it, so once for all, you should be securely logged in without authenticating every time.
(however, every time you restart your app, it will generate a new password)
2. customize your password
Add the following properties to your application.properties if you want to customize your username and password:
security.user.name=myuser
security.user.password=mypassword
And here is how it looks like with your own username and password
Reference:
Spring Boot Features - Security
Monitoring and Management over HTTP
You can bypass this spring boot security mechanism. See an example below for this:
#SpringBootApplication
#EnableAutoConfiguration(exclude = {SecurityAutoConfiguration.class})
public class SampleSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(SampleSpringBootApplication.class, args);
}
}
When Spring Security is in the classpath, Spring Boot by default secures all your pages with Basic authentication. That's why you are being asked for userid and password.
You will need to configure the security. To do so, commonly people would extend a WebSecurityConfigurerAdapter, like this:
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
...
Refer this Spring Security guide for more details.
Here was the issues
(1) .loginPage("/index") was saying my login page was at index, however I just wanted to use spring's default login page.
(2) had to to move the security package inside the demo package (the main package). Thanks to #Sanjay for suggesting that. I tried to use #ComponantScan but it could not get it to work.

How secure Rest-API by token

i use spring data rest 2.1 and would like secure my rest api, what is the best way to secure the api by token. I would have an process like the following steps. Is it also possible to change the base url from / to /api , but my controllers like 'home' would also display the jsp side from /home but not from /api/..
User go to login site
User login and get an token from server
User go to dashboard site and js do ajax call and set HTTP-Header Field 'X_AUTH_TOKEN' with token
You could configure base URI for you API using RepositoryRestConfiguration. Something like this:
#Configuration
public static class RepositoryConfig extends
RepositoryRestMvcConfiguration {
#Override
protected void configureRepositoryRestConfiguration(
RepositoryRestConfiguration config) {
try {
config.setBaseUri(new URI("/api"));
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
}
You could add security using Spring-Security or if you like a simple custom solution use Spring Interceptor