So I have looked around and everywhere says that I need to check my configuration but what doesn't make sense to me is that my app will run on a different computer but the one I use for development. Just to provide some context, this app is purely restful with token authentication. Here's what my config looks like:
Security Init:
#Order(1)
public class SecurityWebappInitializer extends AbstractSecurityWebApplicationInitializer
{
}
App Init:
#Order(2)
public class ApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
{
#Override
protected Class<?>[] getRootConfigClasses()
{
return new Class[]{RootConfiguration.class, SecurityConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses()
{
return new Class<?>[]{WebConfiguration.class};
}
#Override
protected String[] getServletMappings()
{
return new String[]{"/", "/rest/*"};
}
#Override
protected Filter[] getServletFilters()
{
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
return new Filter[] {characterEncodingFilter};
}
}
SecurityConfig:
#EnableWebSecurity
#EnableGlobalMethodSecurity(jsr250Enabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
#Autowired
private NoOpAuthProvider noOpAuthenticationProvider;
#Autowired
private TokenFilter tokenFilter;
public SecurityConfig()
{
super(true);
}
#Override
public void configure(WebSecurity web) throws Exception
{
web
.ignoring()
.antMatchers("/rest/authentication/login")
.antMatchers("/services/**")
.antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.antMatchers("**").hasRole("RUN").and()
.addFilterAfter(tokenFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().and()
.logout();
}
#Bean
public AuthenticationManager authenticationManager() throws Exception
{
List<AuthenticationProvider> authenticationProviders = new ArrayList<AuthenticationProvider>();
authenticationProviders.add(noOpAuthenticationProvider);
return new ProviderManager(authenticationProviders);
}
}
WebConfig:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.example.tinker.web")
public class WebConfiguration extends WebMvcConfigurerAdapter
{
#Autowired
private PrincipalRetrievalService principalRetrievalService;
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
registry.addResourceHandler("/resources/**").addResourceLocations("resources/").setCachePeriod(31556926);
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers)
{
argumentResolvers.add(new WebArgumentResolver(principalRetrievalService));
super.addArgumentResolvers(argumentResolvers);
}
}
Any idea what would be causing my problems?
There is no need to implement AbstractSecurityWebApplicationInitializer.
Security is initialized via ApplicationInitializer/RootConfiguration/#ComponentScan/SecurityConfig. Also there si no need to add SecurityConfig in getRootConfigClasses.
Related
I'm trying to create an oauth2-enabled auth server which is able to authenticate users with two authentication providers: the former is in-memory (for default user-passwords) the latter is an external LDAP server (by now i'm using the example from gs-authenticating-ldap-complete).
I'm able to successfully retrieve an access token for any user, but i'm only able to use the refresh token for retrieving a new token for any user that is registered in the LDAP server. While everything is fine if I try to refresh an in-memory user's token, with the LDAP ones I get: 401 Unauthorized { "error": "unauthorized", "error_description": "ben" } where "ben" is the user id.
As far as I know (after some debugging) the exception occurs in DefaultTokenServices.java:150.
In the following I report the configuration classes I'm using.
#Configuration
#EnableWebSecurity
#Order(6)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().csrf().disable();
}
#Configuration
protected static class DefaultUsersAuthConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN").and().withUser("guest")
.password("guest").roles("USER");
}
}
#Configuration
protected static class LDAPAuthConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication().userDnPatterns("uid={0},ou=people").groupSearchBase("ou=groups")
.userDetailsContextMapper(new MyLdapUserDetailsMapper()).contextSource()
.ldif("classpath:test-server.ldif");
}
}
protected static class MyLdapUserDetailsMapper extends LdapUserDetailsMapper {
#Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username,
Collection<? extends GrantedAuthority> authorities) {
final UserDetails originalUser = super.mapUserFromContext(ctx, username, authorities);
final Set<GrantedAuthority> newAuth = new HashSet<>(originalUser.getAuthorities());
newAuth.add(new SimpleGrantedAuthority("ROLE_EXTRA_ROLE"));
return new User(originalUser.getUsername(), originalUser.getPassword(), originalUser.isEnabled(),
originalUser.isAccountNonExpired(), originalUser.isCredentialsNonExpired(),
originalUser.isAccountNonLocked(), newAuth);
}
}
}
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends OAuth2AuthorizationServerConfiguration {
#Autowired
private TokenStore tokenStore;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("acme").secret("acmesecret")
.authorizedGrantTypes("password", "refresh_token", "client_credentials")
.scopes("read", "write", "openid").autoApprove(true);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore);
}
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.authorizeRequests().antMatchers("/me").authenticated();
// #formatter:on
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(tokenStore);
}
}
}
I'm using spring-boot 1.3.2.RELEASE. What am I missing?
I need to use autowired in a filter. So i annotate my filter class using #Component,
import org.springframework.web.filter.GenericFilterBean;
#Component
public class TokenAuthorizationFilter extends GenericFilterBean {
#Autowired
public EnrollCashRepository enrollCashRepository;
}
Then i add my filter as below in SecurityConfig,
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity webSecurity) throws Exception
{
webSecurity.ignoring().antMatchers(HttpMethod.GET, "/health");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new TokenAuthorizationFilter(), BasicAuthenticationFilter.class);
http.authorizeRequests().antMatchers("/api/**").authenticated();
}
My problem is my filter get invoked twice with the #Component annotation. If i remove the #Component annotation it only invoke once.
Then i add below as a fix in my Spring boot main class. Then i comment the line of addFilterBefore in SecurityConfig.
#Bean
public FilterRegistrationBean tokenAuthFilterRegistration() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new PITokenAuthorizationFilter());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.setEnabled(false);
return filterRegistrationBean;
}
But then my filter get invoked once. But even i make the setEnabled true or false, i get a 403 Forbiddon Error when i invoke my rest api, http://localhost:8080/api/myservice
How can i fix this situation where i can use #Autowired in my Spring Filter?
Edit: Add controller and Filter class,
#RestController
#RequestMapping(value = "/api")
public class SpringToolController {
#RequestMapping(value = "/myservice", method = RequestMethod.GET)
public HttpEntity<String> myService() {
System.out.println("-----------myService invoke-----------");
return new ResponseEntity<String>(HttpStatus.OK);
}
}
public class TokenAuthorizationFilter extends GenericFilterBean {
public TokenAuthorizationFilter(EnrollCashRepository enrollCashRepository) {
this.enrollCashRepository = enrollCashRepository;
}
public EnrollCashRepository enrollCashRepository;
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
System.out.println("before PITokenAuthorizationFilter");
chain.doFilter(servletRequest, servletResponse);
System.out.println("after PITokenAuthorizationFilter");
}
public EnrollCashRepository getEnrollCashRepository() {
return enrollCashRepository;
}
public void setEnrollCashRepository(EnrollCashRepository enrollCashRepository) {
this.enrollCashRepository = enrollCashRepository;
}
}
Remove your FilterRegistrationBean and initialize TokenAuthorizationFilter inside your SecurityConfig like this:
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public EnrollCashRepository enrollCashRepository;
#Override
public void configure(WebSecurity webSecurity) throws Exception
{
webSecurity.ignoring().antMatchers(HttpMethod.GET, "/health");
}
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.addFilterBefore(tokenAuthorizationFilter(), BasicAuthenticationFilter.class);
http.authorizeRequests().antMatchers("/api/**").authenticated();
}
private TokenAuthorizationFilter tokenAuthorizationFilter()
{
return new TokenAuthorizationFilter(enrollCashRepository);
}
}
Remove #Autowired and #Component annotation and set your EnrollCashRepository with constructor injection:
import org.springframework.web.filter.GenericFilterBean;
public class TokenAuthorizationFilter extends GenericFilterBean {
private final EnrollCashRepository enrollCashRepository;
public TokenAuthorizationFilter(EnrollCashRepository enrollCashRepository)
{
this.enrollCashRepository = enrollCashRepository
}
}
I Added a Test Filter to my working class now and it worked fine. Here are the codes related to it.
Filter
#Component
public class TestFilter extends GenericFilterBean {
private static final Logger logger = LoggerFactory.getLogger(TestFilter.class);
#Autowired
UserService userService;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
logger.error("=====================AWESOME=======================");
chain.doFilter(request, response);
userService.activate("123"); //this works
}
}
App Security Config
#Configuration
#EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private TestFilter testFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
//loginFailureHandler.setDefaultFailureUrl("/login?error=true");
http.addFilterBefore(testFilter, BasicAuthenticationFilter.class);
//Http other config here.
}
}
App Config
#Configuration
#ImportResource({
"classpath*:/context.xml"
})
#PropertySources(
#PropertySource({
"classpath:/application.yml"
})
)
#Import({AppSecurityConfig.class, WebConfig.class,TestFilter.class})
public class AppConfig {
}
I have spring mvc project and i don't use #Autowired because my object always is null. How me load JavaConfig for using #Autowired, i do not use any *.xml file.
This is my controller with #Autowired field for service.
#Controller
public class WebController {
#Autowired
private ServiceWeb serviceWeb;
public void setServiceWeb(ServiceWeb serviceWeb) {
this.serviceWeb = serviceWeb;
}
...
}
This is my AbstractAnnotationConfigDispatcherServletInitializer
public class ServletInit extends
AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { SpringRootConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { SpringWebConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
SpringRootConfig & SpringWebConfig
#Configuration
#ComponentScan({"web.controller"})
public class SpringRootConfig {
}
#EnableWebMvc
#Configuration
#ComponentScan({ "web.controller"})
#Import({SecurityConfig.class})
public class SpringWebConfig extends WebMvcConfigurerAdapter{
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
}
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver
= new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
Class for #Autowiring
#Configuration
public class ConfigurationBean {
#Bean
public ServiceWeb serviceWeb(){
return new ServiceWebImpl();
}
}
Register context for spring, but where need write Init.class for loading this config ?
public class Init implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(ConfigurationBean.class);
servletContext.addListener(new ContextLoaderListener(ctx));
ctx.setServletContext(servletContext);
}
}
Pls try this...
#Service
public class ServiceWebImpl implements ServiceWeb {
}
ServiceWeb bean is created inside ConfigurationBean class and its not visible at spring context level for autowiring.
I am trying to go over the following documentation: https://github.com/spring-projects/spring-security-oauth/blob/f25592e682303b0cf89e1d7555174bac18e174df/docs/oauth2.md#mapping-user-roles-to-scopes
In the documentation, it says in order to map user roles to scopes, along with setting the checkUserScopes=true in the DefaultOAuth2RequestFactory, we need to add the TokenEndpointAuthenticationFilter filter after the HTTP BasicAuthenticationFilter. I was wondering how that could be done.
Here is what my AuthorizationServer looks like:
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends
AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private OAuth2RequestFactory requestFactory;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.authenticationManager(authenticationManager);
endpoints.requestFactory(requestFactory);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.withClientDetails(clientDetailsService());
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer.checkTokenAccess("isAuthenticated()");
}
#Bean
public ClientDetailsService clientDetailsService() {
Map<String, ClientDetails> clientDetailsStore = new HashMap<String, ClientDetails>();
Collection<String> scope = new HashSet<String>();
scope.add("user");
scope.add("admin");
Collection<String> authorizedGrantTypes = new HashSet<String>();
authorizedGrantTypes.add("password");
authorizedGrantTypes.add("refresh_token");
BaseClientDetails clientDetails = new BaseClientDetails();
clientDetails.setClientId("client");
clientDetails.setClientSecret("secret");
clientDetails.setScope(scope);
clientDetails.setAuthorizedGrantTypes(authorizedGrantTypes);
clientDetailsStore.put("client", clientDetails);
InMemoryClientDetailsService clientDetailsService = new InMemoryClientDetailsService();
clientDetailsService.setClientDetailsStore(clientDetailsStore);
return clientDetailsService;
}
#Bean
public OAuth2RequestFactory requestFactory() {
DefaultOAuth2RequestFactory requestFactory =
new DefaultOAuth2RequestFactory(clientDetailsService());
requestFactory.setCheckUserScopes(true);
return requestFactory;
}
}
Also, it would be fantastic to provide a sample CURL on how we can test the grant-type password.
Appreciate any help!
Instead of using #EnableAuthorizationServer you should be able to extend AuthorizationServerSecurityConfiguration and include that in your Spring configuration. E.g.
#Configuration
public class OAuth2Config extends AuthorizationServerSecurityConfiguration {
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.addFilterAfter(myFilter(), BasicAuthenticationFilter.class);
}
}
You can add also add additional filters via the AuthorizationServerSecurityConfigurer, though they come before Basic auth, not after.
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.addTokenEndpointAuthenticationFilter(myFilter());
security.checkTokenAccess("isAuthenticated()");
}
Adds a new custom authentication filter for the TokenEndpoint. Filters will be set upstream of the default BasicAuthenticationFilter.
I cannot seem to get simple Spring application to work with JavaConfig.
public class WebApp extends AbstractAnnotationConfigDispatcherServletInitializer {
private static final Logger logger = Logger.getLogger(WebApp.class);
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[0];
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{ WebAppConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[]{ "/" };
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
logger.debug("onStartup");
super.onStartup(servletContext);//MUST HAVE
servletContext.setInitParameter("defaultHtmlEscape", "true");
}
#Configuration
#EnableWebMvc
#ComponentScan("com.doge.controller")
public static class WebAppConfig extends WebMvcConfigurerAdapter {
}
}
And controller:
package com.doge.controller;
#RestController
public class HelloController {
#RequestMapping("/")
public String sayHello() {
System.out.println("something");
return "index";
}
}
I always get 404 on "localhost:8080/Build" nor "localhost:8080".
Nothing is ever logged nor printed, just "INFO: Server startup in 538 ms".
There are few options of initialize Spring web application. The easiest is like below:
public class SpringAnnotationWebInitializer extends AbstractContextLoaderInitializer {
#Override
protected WebApplicationContext createRootApplicationContext() {
AnnotationConfigWebApplicationContext applicationContext =
new AnnotationConfigWebApplicationContext();
applicationContext.register(WebAppConfig.class);
return applicationContext;
}
}
Other options can be found here: http://www.kubrynski.com/2014/01/understanding-spring-web-initialization.html