I have a working Spring Boot 1.3.1 application with an AngularJS client using the "password" grant type with OAuth2. I know this is not very good as the client_id and client_secret are visible in the AngularJS code by everybody.
I would like to change to the "implicit" grant. My authorisation server and resource server are running in the same application.
This is my current configuration of the authorisation server:
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
private MyApplicationSecuritySettings securitySettings;
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private PasswordEncoder passwordEncoder;
#Autowired
private DataSource dataSource;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient(securitySettings.getAngularClientId())
.authorizedGrantTypes("password","refresh_token")
.scopes("read", "write")
.resourceIds(RESOURCE_ID)
.secret(passwordEncoder.encode(securitySettings.getAngularClientSecret()));
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore())
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.passwordEncoder(passwordEncoder);
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(tokenStore());
return tokenServices;
}
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource) {
// Workaround for https://github.com/spring-projects/spring-boot/issues/5071
#Override
protected OAuth2Authentication deserializeAuthentication(byte[] authentication) {
ObjectInputStream input = null;
try {
input = new ConfigurableObjectInputStream(
new ByteArrayInputStream(authentication),
Thread.currentThread().getContextClassLoader());
return (OAuth2Authentication) input.readObject();
} catch (Exception ex) {
throw new IllegalArgumentException(ex);
} finally {
if (input != null) {
try {
input.close();
} catch (IOException ex) {
// Continue
}
}
}
}
};
}
}
I also have this WebSecurityConfigurerAdapter subclass:
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private PasswordEncoder passwordEncoder;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder);
}
#Override
public void configure(WebSecurity web) throws Exception {
// Disable security for bower components
web.ignoring().antMatchers("/components/**");
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
and this is the configuration of the resource server:
#Configuration
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin", "/admin/", "/index.html", "/home.html", "/", "/logout",
"/partials/login.html"
)
.permitAll()
.authorizeRequests()
.antMatchers("/management/**").hasRole("ADMIN")
.anyRequest().authenticated();
}
}
With this configuration, a token can be requested via /oauth/token. This is implemented in AngularJS when the user visits http://localhost:8080/admin/#/login and types his username and password in that HTML form.
For the implicit grant, I am changing the ClientDetailsServiceConfigurer to:
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient(securitySettings.getAngularClientId())
.authorizedGrantTypes("implicit")
.scopes("read", "write")
.resourceIds(RESOURCE_ID);
}
If I then try the following url: http://localhost:8080/oauth/authorize?client_id=angularClient&response_type=token
The browser redirects to http://localhost:8080/login and shows:
<oauth>
<error_description>Full authentication is required to access this resource</error_description>
<error>unauthorized</error>
</oauth>
Am I testing this implicit flow wrongly?
Do I need to change something in the configuration so that the redirect goes to http://localhost:8080/admin/#/login instead? Or does the angular app need to go from http://localhost:8080/admin/#/login to http://localhost:8080/oauth/authorize passing in the user's username and password somewhere?
Related
I am currently working on a Spring Boot application and I have the task to do the security of the application. They suggested to use OAuth2 token authentification even thought in other applications I manage to create the security with other spring security tutorial.
This are created based on tutorials I found on different sources:
public class OAuthPermissionConfig extends ResourceServerConfigurerAdapter
#Override
public void configure(HttpSecurity http) throws Exception {
http.anonymous().disable()
.authorizeRequests()
.antMatchers("/pim/oauth/token").permitAll().and().formLogin()
.and().authorizeRequests().antMatchers("/actuator/**", "/v2/api-docs", "/webjars/**",
"/swagger-resources/configuration/ui", "/swagger-resources", "/swagger-ui.html",
"/swagger-resources/configuration/security").hasAnyAuthority("ADMIN")
.anyRequest().authenticated();
}
public class CustomAuthenticationProvider implements AuthenticationProvider
#Autowired
private ADService adService;
#Autowired
private UserService userService;
#Override
#Transactional
public Authentication authenticate(Authentication authentication) {
try {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
User user = userService.getUserByUsername(username);
userService.isUserAllowedToUseTheApplication(user);
if (adService.isUserNearlyBlockedInAD(user)) {
throw new BadCredentialsException(CustomMessages.TOO_MANY_LOGIN_FAILED);
} else {
adService.login(username, password);
}
List<GrantedAuthority> userAuthority = user.getRoles().stream()
.map(p -> new SimpleGrantedAuthority(p.getId())).collect(Collectors.toList());
return new LoginToken(user, password, userAuthority);
} catch (NoSuchDatabaseEntryException | NullArgumentException | NamingException | EmptyUserRolesException e) {
throw new BadCredentialsException(CustomMessages.INVALID_CREDENTIALS + " or " + CustomMessages.UNAUTHORIZED);
}
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(
UsernamePasswordAuthenticationToken.class);
}
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
public class OAuthServerConfig extends AuthorizationServerConfigurerAdapter
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private UserService userService;
#Autowired
private PasswordEncoder passwordEncoder;
#Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.authenticationManager(authenticationManager).tokenEnhancer(tokenEnhancer());
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("pfjA#Dmin")
.secret(passwordEncoder.encode("4gM~$laY{gnfShpa%8Pcjwcz-J.NVS"))
.authorizedGrantTypes("password")
.accessTokenValiditySeconds(UTILS.convertMinutesToSeconds(1440))
.scopes("read", "write", "trust")
.resourceIds("oauth2-resource");
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.checkTokenAccess("isAuthenticated()").allowFormAuthenticationForClients();
}
When testing the login, I use postman with this parameters :
http://localhost:8080/oauth/token?grant_type=password
Headers: Basic btoa(pfjA#Dmin,4gM~$laY{gnfShpa%8Pcjwcz-J.NVS)
Content-Type : application/x-www-form-urlencoded
Body: form-data -> username and pass
that should be a valid user credentials from the database.
And the user will respond if the credentials are correct
"access_token": "f0dd6eee-7a64-4079-bb1e-e2cbcca6d7bf",
"token_type": "bearer",
"expires_in": 86399,
"scope": "read write trust"
Now I have to use this token for all the other requests otherwise I dont have any permision to use the application.
My question: Is this other version of Spring Security or what? I read about OAuth2 authentication but I read that an application can have BOTH Spring Security and OAuth2. Can someone please explain me if there is something wrong with the way we decided to implement the app security?
Thank you very much!
Yes,you can think it's a different version of spring security,it replaces some strategies of standard spring security,such as the authorization checking of requests.
I configured resource and authorization servers in one application. I use spring-security oauth2, with Resource Owner Password Credentials. Can I set up basic auth on the server side? I don't want to do it on the front-end.
I don't know what a part of the code I need to show...
When I want to receive a token I need to enter this in postman:
Can I configure it on the server side?
Authorization Server:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
private TokenStore tokenStore;
#Autowired
private JwtAccessTokenConverter jwtTokenEnhancer;
#Autowired
private UserApprovalHandler userApprovalHandler;
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore).tokenEnhancer(jwtTokenEnhancer).userApprovalHandler(userApprovalHandler)
.authenticationManager(authenticationManager)
.pathMapping("/oauth/token", "/login");
}
}
Resource Server:
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "resource_id";
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(false);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/swagger-ui.html#").permitAll()
.antMatchers("/").authenticated()
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
Security config:
#Configuration
#EnableWebSecurity(debug = true)
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private ClientDetailsService clientDetailsService;
#Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(10);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
public void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtTokenEnhancer());
}
#Bean
protected JwtAccessTokenConverter jwtTokenEnhancer() {
converter.setSigningKey("Demo-Key-1");
return converter;
}
#Bean
#Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore) {
TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore);
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
return handler;
}
#Bean
#Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
#Override
#Order(Ordered.HIGHEST_PRECEDENCE)
protected void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll()
.and().cors().and().csrf().disable();
}
}
This answer is accompanied by a complete and working sample.
Maybe you are biting off more than you can chew here?
For example:
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll()
The /oauth/token endpoint must remain protected. This is the endpoint on the authorization server that issues tokens to authenticated clients. The system will probably fail with NullpointerException or other exceptions if you open it, however, the above configuration option indicate that maybe you're a bit confused about how OAuth2 works.
What I would recommend is to first fully understand authorization server versus resource server. You can definitely combine the two, but they would have very different endpoints.
Authorization Server - typical endpoints
/oauth/token - issues tokens
/oauth/authorize - issues authorization codes
/introspect - validates a token and returns token claims in a known format
Resource Server - These would be your application endpoints, requiring Bearer tokens, for example
/account/123/debit
and these endpoints expect a stateless request that has an authorization header
Authorization: Bearer <token value here>
A controller for a resource server would look like this:
#PreAuthorize("hasRole('your-scope-role')")
#RequestMapping(value = "/hello")
#ResponseBody
public String hello(Principal principal) {
return "Hello to " + principal.getName();
}
Feel free to review the simple project that I have created for you.
In addition to that, I also recommend this video on OAuth2 and OpenID Connect
In my sample, I have configured the clients like this:
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
InMemoryClientDetailsService clientDetails = new InMemoryClientDetailsService();
BaseClientDetails client = new BaseClientDetails(
"testclient",
null,
"testscope,USER,ADMIN",
"password",
null
);
client.setClientSecret(passwordEncoder.encode("secret"));
clientDetails.setClientDetailsStore(
Collections.singletonMap(
client.getClientId(),
client
)
);
clients.withClientDetails(clientDetails);
}
Take a look at this simple test case, the client is using http-basic authentication:
mvc.perform(
post("/oauth/token")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.param("username", "admin")
.param("password", "password")
.param("grant_type", "password")
.param("response_type", "token")
.param("client_id", "testclient")
.header("Authorization", "Basic " + Base64.encodeBase64String("testclient:secret".getBytes()))
This is client authentication, using the http-basic method:
.header("Authorization", "Basic " + Base64.encodeBase64String("testclient:secret".getBytes()))
I've been developing Spring Cloud (with Netflix OSS stack) microservices architecture for some time. As you would expect, I've separated authorization server as a stand alone microservice. My front end application uses "password" grant type for user login purposes. However, I'm using "client-credentials" grant type for the rest calls that I make from front-end service to other back-end services. Client-credentials grant type is being used among other back-end services as well. By doing so, I am not able to get who is the actual invoker (currently logged in user) of a request. Is there a way to inject authentication and authorization info of the principal to the token that is being issued in client-credentials grant?
My Authorization Server Config class
#Configuration
#EnableAuthorizationServer
#Order(Ordered.HIGHEST_PRECEDENCE)
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("testclient")
.secret("{noop}testsecret")
.authorizedGrantTypes("authorization_code","password","client_credentials")
.scopes("ui")
.autoApprove(true)
// .accessTokenValiditySeconds(3600)
.and()
.withClient("backend-service")
.secret("{noop}backendsecret")
.authorizedGrantTypes("client_credentials","refresh_token")
.scopes("server")
.autoApprove(true)
}
#Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
endpoints.tokenEnhancer(tokenEnhancer());
endpoints.tokenStore(tokenStore());
}
#Bean
public TokenStore tokenStore() {
//return new JdbcTokenStore(dataSource);
return new InMemoryTokenStore();
}
#Bean
#Primary
public AuthorizationServerTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenEnhancer(tokenEnhancer());
tokenServices.setTokenStore(tokenStore());
return tokenServices;
}
#Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
Security Config Class
#Configuration
#Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.requestMatchers()
.antMatchers("/login", "/oauth/authorize")
.and()
.authorizeRequests()
.antMatchers("/resources/**", "/src/main/webapp/**","/css/**","/images/**").permitAll()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll().and().httpBasic().disable();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/static/**","/resources/**", "/src/main/webapp/**","/css/**","/images/**");
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("{noop}a1b2c3#").roles("User");
}
}
I've tried to implement a Token Enhancer class to propogate additional data in token. However, I don't think this is the correct and secure way for what I'm trying to achieve.
public class CustomTokenEnhancer implements TokenEnhancer {
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
final Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("customInfo", "testdata");
((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(additionalInfo);
return oAuth2AccessToken;
}
}
Your assistance would be appreciated.
If you are using an oauth token generated using Client Credentials then you can not get user information. You can only get source of the request (client).
If want user information across micro services then you have to use password grant type to generate oauth token.
UPDATE
(Answer by Chids is for the problem that I posted earlier which was getting 403 error for /oauth/token. That error is resolved and am stuck at the next step .I have modified the question accordingly.)
Problem:
I am trying to implement OAuth 2.0 with Spring security. And I am successful in obtaining the access_token by making a post request to /oauth/token.
But when I use this access token to use any other secured url I am getting 403.
I have followed multiple questions on SO but all of them suggest to disable csrf for my problem. Issue is I have already disabled that but still getting error.
Can someone guide me whether I am constructing the post request in a wrong way or whether some config is missing.
My post request through postman looks like:
Config on google:
Resource Server
#Configuration
#EnableResourceServer
#Order(3)
public class Oauth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.requestMatchers().antMatchers("/auth/**")
.and()
.authorizeRequests()
.antMatchers("/auth/**").authenticated();
}
}
Authorization server
#Configuration
#EnableAuthorizationServer
public class Oauth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Autowired
private UserApprovalHandler handler;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authManager;
#Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("568176070083-1lc20949a0q58l0rhmq93n95kvu8s5o6.apps.googleusercontent.com")
.secret("lNfK3wOaVibgu96il6WLrkTh")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.accessTokenValiditySeconds(120)
.refreshTokenValiditySeconds(600);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore).userApprovalHandler(handler)
.authenticationManager(authManager);
}
}
Security Config
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true)
#ComponentScan(basePackages = "com.saml.demo")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
.and()
.withUser("admin").password("admin123").roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/oauth/token").permitAll()
.anyRequest().authenticated();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Bean
#Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
}
It should be because, you are disabling all anonymous access in your configure block. You can change it to the following
#Override
protected void configure(final HttpSecurity http) throws Exception {
// #formatter:off
http.authorizeRequests().antMatchers("/login").permitAll().antMatchers("/oauth/token/revokeById/**").permitAll()
.antMatchers("/tokens/**").permitAll().anyRequest().authenticated().and().formLogin().permitAll().and()
.csrf().disable();
// #formatter:on
}
I just started using Spring boot and Java Based Spring Configurations and I have a problem when trying to test security-related methods such as login and logout.
I have the following configurations in my project
SecurityConfig.java:
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint())
.and()
.formLogin()
.successHandler(authenticationSuccessHandler())
.failureHandler(authenticationFailureHandler())
.and()
.logout()
.and()
.authorizeRequests()
.antMatchers("/login", "j_spring_security_check", "/register", "/logout").permitAll()
.antMatchers("/**").hasRole("USER");
}
#Autowired
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("guest").password("guest").roles("USER");
}
#Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() {
return new RestAuthenticationSuccessHandler();
}
#Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
return new SimpleUrlAuthenticationFailureHandler();
}
#Bean
public AuthenticationEntryPoint authenticationEntryPoint() {
return new RestAuthenticationEntryPoint();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
SecurityWebAppInitializer.java
#Order(1)
public class SecurityWebAppInitializer extends AbstractSecurityWebApplicationInitializer { }
RestAuthenticationSuccessHandler.java
public class RestAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private RequestCache requestCache = new HttpSessionRequestCache();
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
SavedRequest savedRequest = requestCache.getRequest(request, response);
if (savedRequest == null) {
clearAuthenticationAttributes(request);
return;
}
String targetUrlParam = getTargetUrlParameter();
if (isAlwaysUseDefaultTargetUrl() ||
(targetUrlParam != null &&
StringUtils.hasText(request.getParameter(targetUrlParam)))) {
requestCache.removeRequest(request, response);
clearAuthenticationAttributes(request);
return;
}
clearAuthenticationAttributes(request);
}
public void setRequestCache(RequestCache requestCache) {
this.requestCache = requestCache;
}
}
Now, I want to test the login process so my test class is the following:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { SecurityWebAppInitializer.class, SecurityConfig.class })
public class SecurityTests {
private MockMvc mockMvc;
#Autowired
private FilterChainProxy filterChainProxy;
#Before
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(SecurityWebAppInitializer.class, SecurityConfig.class).addFilters(filterChainProxy).build();
}
#Test
public void thatLoginWithCorrectCredentialsWorks() throws Exception {
mockMvc.perform(post("/j_spring_security_check")
.param("j_username", "guest")
.param("j_password", "guest")
).andExpect(status().isOk());
}
}
The above test returns the following error
java.lang.AssertionError: Status
Expected :200
Actual :403
which says that the user is forbidden to access the login page. However I don't get why as I've configured in the SpringConfig that a user with the above credentials is allowed to login to my application. I would like to apologise in advance in case my question is silly but I am still trying to improve my knowledge on that framework as I am using it only for a couple of weeks.
EDIT: When I am using CURL to login I am getting the following error:
{"timestamp":1401633376720,"error":"Forbidden","status":403,"message":"Expected CSRF token not found. Has your session expired?"}