I setting the Spring Configuration Below:
#EnableAuthorizationServer
#EnableWebSecurity
#Configuration
public class Oauth2Provider extends WebSecurityConfigurerAdapter implements
AuthorizationServerConfigurer {
/*
* #Autowired private TokenStore tokenStore;
*/
#Configuration
protected static class AuthenticationConfiguration extends
GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password")
.roles("USER").and().withUser("admin").password("password")
.roles("USER", "ADMIN");
}
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security)
throws Exception {
// TODO Auto-generated method stub
security.allowFormAuthenticationForClients();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
// TODO Auto-generated method stub
clients.inMemory()
.withClient("my-trusted-client")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT", "ROLE_ANONYMOUS")
.scopes("read", "write", "trust")
.secret("secret")
.accessTokenValiditySeconds(60);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
// TODO Auto-generated method stub
}
}
And Maven Setting is Below:
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
I Access :
http://localhost:8080/oauth/token
Payload
grant_type=password&password=password&username=user&scope=read&client_id=my-trusted-client&client_secret=secret
But I receive error below:
{
error: "unsupported_grant_type"
error_description: "Unsupported grant type: password"
}
To use password grant you need to provide an authentication manager to the authorization server (in the empty method with the TODO in your example), so it can authenticate users. If it's a Spring Boot application there is always an AuthenticationManager available to be #Autowired.
Related
I am using Spring Security with JWT for authenticating rest apis. On login, the JWT token is generated and shared to the mobile client. But, the token in the subsequent requests are not been validated. Is there anything wrong in the security configuration ?
Spring security version - 5.1.6.RELEASE
// Security Configuration
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private JwtTokenAuthenticationFilter jwtTokenAuthenticationFilter;
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.
httpBasic().disable().
csrf().disable().
exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).
and().
addFilterBefore(jwtTokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class).
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).
and().
authorizeRequests().antMatchers("/user/login").permitAll().
antMatchers("/user/test").authenticated().
anyRequest().authenticated();
}
}
// JWT Token Auth Filter - This is never invoked
#Component
public class JwtTokenAuthenticationFilter extends GenericFilterBean {
#Autowired
private JwtTokenProvider jwtTokenProvider;
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {
String token = jwtTokenProvider.resolveToken((HttpServletRequest) req);
if (null != token && jwtTokenProvider.validateToken(token)) {
Authentication auth = jwtTokenProvider.getAuthentication(token);
if (null != auth) {
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
filterChain.doFilter(req, res);
}
}
I was expecting that all the requests after login will be authenticated against the JWT token.
I tried putting the name of the service to be authenticated as below:
antMatchers("/user/test").authenticated().
Also, any request authenticated is also added, but neither of them worked.
anyRequest().authenticated();
Try to replace
addFilterBefore(jwtTokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class).
by
addFilterBefore(jwtTokenAuthenticationFilter), BasicAuthenticationFilter.class);
If not runs, change jwtTokenAuthenticationFilter class to avoid to be a Spring bean and use like this:
addFilterBefore(new JwtTokenAuthenticationFilter(jwtTokenProvider)), BasicAuthenticationFilter.class);
And add the following code on Security Class:
#Autowired
private JwtTokenProvider jwtTokenProvider;
I am having following configuration in Spring Security wiht OAuth2:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
//#EnableOAuth2Sso
public class FVSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user")
.password("secret")
.roles("USER", "ROLE1")
.and()
.withUser("admin")
.password("password")
.roles("ADMIN","USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/api/**").hasRole("USER")
.anyRequest().authenticated()
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler())
.and()
.formLogin();
}
Resource server configuraion:
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.inMemory()
.withClient("sampleClient")
.authorizedGrantTypes("implicit")
.scopes("read")
.autoApprove(true)
.accessTokenValiditySeconds(30)
.and()
.withClient("user")
.secret("secret")
.scopes("read", "write")
.autoApprove(true)
.authorizedGrantTypes(
"password","authorization_code", "refresh_token")
.and()
.withClient("admin")
.secret("password")
.scopes("read", "write", "custom")
.autoApprove(true)
.authorizedGrantTypes(
"password","authorization_code", "refresh_token");
}
And the following Rest Controller
#RestController
public class OAuthTestController {
#PreAuthorize("hasRole('ROLE_ROLE1') and #oauth2.hasScope('read')")
#RequestMapping(value="/api/user/test1", method = RequestMethod.GET)
public String testGETWithRole() {
return "[GET] Needs role ROLE1";
}
#RequestMapping(value="/api/admin/test1", method = RequestMethod.GET)
public String testWithAdminRole() {
return "[GET] Needs Admin role";
}
And the resource server:
#Configuration
#EnableResourceServer
public class ResourceServer extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "my_rest_api";
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(false);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.
anonymous().disable()
.requestMatchers().antMatchers("/api/**")
.and().authorizeRequests()
.antMatchers("/api/**").access("hasRole('USER')")
.antMatchers("/api/admin/**").access("hasRole('ADMIN')")
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
Basically I am expecting Spring to use OAuth2 for authentication and use spring security roles to allow resource access.
I generated and admin token and afterwards an user token. I am expecting when using admin token to be able to access /api/admin resource and when using user token to not be able.
I just noticed that the form login is not prompted always, so I suppose Spring remembers the authenticated user.
When I used the admin token I got access denied. When debugging in Spring sources I noticed that the authentication was an instance of UserNamePasswordAuthenticationPassword with the user details, not admin as expected. I suppose is because last time I used those credentials in login form.
Is it a way to force spring to use credentials for corresponding token, or to force Spring show login form each time?
P.S. I use Postman to test the API.
Thanks.
I have some Spring RESTful (RestControllers) web services with no web.xml and I am using Spring boot to start the services.
I want to add authorization layer for the web services and wanted to route all the http requests to one front controller before actually calling the web service itself. (I have a code to simulate sessions behavior at the autherisation layer, to validate a user based on a generated key that I send with each of the httpRequest from the client).
Is there any Standard Spring solution on routing all the requests to a filter /front controller?
Thanks in advance,
Praneeth
Edit:
Adding my code
Controller:
`
#RestController
public class UserService {
UserDAO userDAO = new UserDAO();
#RequestMapping(value="/login", method = RequestMethod.POST)
#LoginRequired
public String login(#RequestParam(value="user_name") String userName, #RequestParam(value="password") String password, HttpServletRequest request){
return userDAO.login(userName, password);
}
}`
Interceptor:
`
public class AuthenticationInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("In Interceptor");
//return super.preHandle(request, response, handler);
return true;
}
#Override
public void postHandle( HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("---method executed---");
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
System.out.println("---Request Completed---");
}
}
`
Interface.
`
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface LoginRequired {
}
`
Following steps can be taken to implement the interceptor with Spring:
Implement an interceptor class extending HandlerInterceptorAdapter class. Following is how the code could look like:
public class LoginInterceptor extends HandlerInterceptorAdapter {
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception)
throws Exception {
// TODO Auto-generated method stub
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
// TODO Auto-generated method stub
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
String emailAddress = request.getParameter("emailaddress");
String password = request.getParameter("password");
if(StringUtils.isEmpty(emailAddress) || StringUtils.containsWhitespace(emailAddress) ||
StringUtils.isEmpty(password) || StringUtils.containsWhitespace(password)) {
throw new Exception("Invalid User Id or Password. Please try again.");
}
return true;
}
}
Implement an AppConfig class or add the addInterceptors in one of the existing Configuration class. Note the path pattern specified with the LoginInterceptor instance
#Configuration
public class AppConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/account/login");
}
}
Implement the controller method such as following:
#Controller
#RequestMapping("/account/login")
public class LoginController {
#RequestMapping(method = RequestMethod.GET)
public String login() {
return "login";
}
}
here an example of Interceptor :
public class AuthenticationInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
LoginRequired loginRequired = handlerMethod.getMethod().getAnnotation(LoginRequired.class);
if (loginRequired == null) {
return true;
}
String token = httpServletRequest.getParameter("token");
if (StringUtils.isBlank(token)) {
throw new MissingParameterException();
}
authenticationService.checkToken(token);
return super.preHandle(httpServletRequest, httpServletResponse, handler);
}
#Override
public void postHandle( HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("---method executed---");
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
System.out.println("---Request Completed---");
}
We can create an annotation :
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface LoginRequired {
}
And then on controller, we had this annotation :
#RequestMapping(value = "/protected/controller")
#LoginRequired
public ResponseEntity<BaseResponse> controller() {
...
}
This is just a template/example to give you an idea.
I hope this will help you.
There is a default solution for such things.
spring security. And you will just have to implement something like:
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error")
.usernameParameter("email")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.permitAll();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(new BCryptPasswordEncoder());
}
}
the dependency for it is:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
After Spring 5 :
Implementation should be like this: We should have a class that implements
HandlerInterceptor
public class CustomInterceptor implements HandlerInterceptorr{
}
Then we can register this interceptor by a class that implements WebMvcConfigurer
and override the method addInterceptors
public class ServiceInterceptorAppConfig implements WebMvcConfigurer {
#Autowired
CustomInterceptor customInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(customInterceptor);
}
}
You should add this to regsiter your interceptor
#Configuration
public class MyConfiguration extends WebMvcConfigurerAdapter {
#Bean
AuthenticationInterceptor getAuthenticationInterceptor() {
return new AuthenticationInterceptor();
}
#Override
public void addInterceptors (InterceptorRegistry registry) {
registry.addInterceptor(getAuthenticationInterceptor());
}
}
If you looking for simple answer for spring boot application..
public class HttpInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// do something here.
return true;
}
}
and
#Configuration
public class AppConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HttpInterceptor());
// registry.addInterceptor(new HttpInterceptor()).addPathPatterns("/account/login"); you can add specific end point as well.
}
}
I try to create spring rest service, whis is autenticated by my own oauth2 resources server. I created resource server:
#Configuration
#EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(tokenStore).resourceId("mobileapp");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/api/shop /**").authenticated().and()
.authorizeRequests().antMatchers("/auth/**").anonymous();
}
}
and authorization server:
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager auth;
#Autowired
private DataSource dataSource;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
#Bean
public JdbcTokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Bean
protected AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.passwordEncoder(passwordEncoder);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authorizationCodeServices(authorizationCodeServices())
.authenticationManager(auth)
.tokenStore(tokenStore())
.approvalStoreDisabled();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource)
.passwordEncoder(passwordEncoder);
.withClient("mobile")
.authorizedGrantTypes("password", "refresh_token")
.authorities("ROLE_CLIENT")
.scopes("read", "write", "trust")
.autoApprove(true)
.resourceIds("mobileapp")
.secret("123456");
}
When I try to receive an access token from server, using curl:
curl -X POST -vu mobile:123456 http://localhost:8080/oauth/token -H
"Accept: application/json" -d
"password=test123&username=admin#gmail.com&grant_type=password&scope=read&client_secret=123456&client_id=mobile"
I get this error as a response message:
{"error":"server_error","error_description":"java.io.NotSerializableException:
org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"}
In tomcat logs there is also
o.s.s.o.p.token.store.JdbcTokenStore - Failed to find access token for
token
EDIT:
Bean definition of password encoder:
#Bean
public BCryptPasswordEncoder passwordEncoder() {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
return bCryptPasswordEncoder;
}
This bean is created in class, in which OAuth2Config and ResourceServer are declared.
I checked code and found out which table spring uses and the table is empty. My question is: should it be auto generated or there is a problem with my code?
Thanks in advance for help.
Override JdbcTokenStore class and replace this function with.
public OAuth2AccessToken readAccessToken(String tokenValue) {
OAuth2AccessToken accessToken = null;
try {
accessToken = new DefaultOAuth2AccessToken(tokenValue);
}
catch (EmptyResultDataAccessException e) {
if (LOG.isInfoEnabled()) {
LOG.info("Failed to find access token for token "+tokenValue);
}
}
catch (IllegalArgumentException e) {
LOG.warn("Failed to deserialize access token for " +tokenValue,e);
removeAccessToken(tokenValue);
}
return accessToken;
}
Your problem of failed to find access token is resolved.Use this class in OAuth2Config.
Your model must have BCryptPasswordEncoder which is not serialized. Make it transient in your user bmodel.
private transient BCryptPasswordEncoder passwordEncoder;
The solution, as far as Postgres is concerned, use BYTEA for ALL token and authentication columns.
The columns are defined as LONGVARBINARY in this schema reference: https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql
In other words, replace LONGVARBINARY with BYTEA if you are using Postgres.
Cheers
I am using spring security via spring boot.
I have two kinds of rest services.
public/** --> Every one can access and use these services
secure/** --> Only authenticated users can use.
#Slf4j
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity.ignoring().antMatchers("/public/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(requestHeaderAuthenticationFilter(authenticationManager()),
BasicAuthenticationFilter.class)
.authorizeRequests().antMatchers("/secure/**").fullyAuthenticated();
}
#Bean
public RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter(
final AuthenticationManager authenticationManager) {
RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager);
filter.setExceptionIfHeaderMissing(true);
filter.setPrincipalRequestHeader("MY_HEADER");
filter.setInvalidateSessionOnPrincipalChange(true);
filter.setCheckForPrincipalChanges(false);
filter.setContinueFilterChainOnUnsuccessfulAuthentication(false);
return filter;
}
When i want to access a resource under public i got exception.
exception: "org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException"
message: "MY_HEADER header not found in request."
Why does my filter activated under public resource while it is configured as ignored resource?
Thanks is advance
This is an issue in WebSecurity.ignoring() as discussed in Spring Security Github when using Beans as Filters.
You can work around this by removing the #Bean annotation in your Filter declaration.
// #Bean - Remove or Comment this
public RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter(
final AuthenticationManager authenticationManager) {
RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager);
filter.setExceptionIfHeaderMissing(true);
filter.setPrincipalRequestHeader("MY_HEADER");
filter.setInvalidateSessionOnPrincipalChange(true);
filter.setCheckForPrincipalChanges(false);
filter.setContinueFilterChainOnUnsuccessfulAuthentication(false);
return filter;
}