I am trying to implement Method Security using #PreAuthorize.
Spring Version: 4.2.3.Release
Spring Security: 4.0.3.Release
I have implemented a CustomPermissionEvaluator. I have noticed that it seems to be working fine except for 1 service where the hasPmerission is not called.
I know this because I get the a logging message from hasPermission / or in the erroneous case do not get the log:
public boolean hasPermission(Authentication authentication, Object o, Object o1) {
logger.info("Call to hasPermission with "+o+" and "+o1);
...
}
My Spring configuration is as follows:
#Configuration
#ComponentScan
public class RootConfiguration {
}
MVC Config
#EnableWebMvc
#Configuration
#ComponentScan({"OntoRais.*"})
#PropertySource("classpath:application.properties")
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class MvcConfiguration extends WebMvcConfigurerAdapter{
#Bean
public ViewResolver getViewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigIn() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean(name="multipartResolver")
public CommonsMultipartResolver commonsMultipartResolver(){
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
commonsMultipartResolver.setDefaultEncoding("utf-8");
commonsMultipartResolver.setMaxUploadSize(50000000);
return commonsMultipartResolver;
}
}
Method Security Config:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#ComponentScan
public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Autowired
private CustomPermissionEvaluator permissionEvaluator;
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler handler
= new DefaultMethodSecurityExpressionHandler();
handler.setPermissionEvaluator(permissionEvaluator);
return handler;
}
public CustomPermissionEvaluator getPermissionEvaluator() {
return permissionEvaluator;
}
public void setPermissionEvaluator(CustomPermissionEvaluator permissionEvaluator) {
this.permissionEvaluator = permissionEvaluator;
}
}
Initializer:
#Configuration
#EnableSpringConfigured
public class MessageWebApplicationInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.addListener(org.springframework.web.context.request.RequestContextListener.class);
super.onStartup(servletContext);
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { MvcConfiguration.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
#Override
protected Filter[] getServletFilters() {
return new Filter[]{new HiddenHttpMethodFilter(),
new OpenEntityManagerInViewFilter(),
new DelegatingFilterProxy("springSecurityFilterChain")
};
}
}
Security Config:
#Configuration
#EnableWebSecurity
#ComponentScan
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
OntoRAISUserDetailsService ontoRAISUserDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
formLogin().
and().
logout().
and().
authorizeRequests().
antMatchers("/login").permitAll().
anyRequest().authenticated().
and().csrf().disable();
}
#Autowired
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(ontoRAISUserDetailsService);
auth.authenticationProvider(authenticationProvider());
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(ontoRAISUserDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
public OntoRAISUserDetailsService getOntoRAISUserDetailsService() {
return ontoRAISUserDetailsService;
}
public void setOntoRAISUserDetailsService(OntoRAISUserDetailsService ontoRAISUserDetailsService) {
this.ontoRAISUserDetailsService = ontoRAISUserDetailsService;
}
The Service in question:
#Service
public class StakeholderService {
#Autowired
private OntopManager om;
private static final Logger logger = LoggerFactory.getLogger("OntoRais");
public OntopManager getOm() {
return om;
}
public void setOm(OntopManager om) {
this.om = om;
}
#PreAuthorize("hasPermission(#stakeholderType, 'Create_StakeholderType')")
public void createStakeholderType(StakeholderType stakeholderType) {
try {
logger.info("Create stakeholder type in service layer");
List<OBDADataSource> sources = om.getObdaModel().getSources();
OBDAMappingAxiom mapping = om.getObdaModel().getMapping(new URI("genertatedURI"), MappingList.StakheholderType());
HashMap<String, String> values = new HashMap<>();
values.put("stakeholderName", stakeholderType.getLabel());
String query = ClassSQLHelper.generateSQLCreateSatement(mapping.getSourceQuery(), values);
SQLHelper.executeSQL(query, sources.get(0));
} catch (URISyntaxException e) {
logger.error(e.getMessage());
}
}
And the controller from which i call the service layer:
#Api(description = "Operations related to Stakeholders")
#RestController
public class StakeholderController {
#Autowired
private OntopManager om;
#Autowired
StakeholderService stakeholderService;
#Autowired
ProjectService projectService;
private static final Logger logger = LoggerFactory.getLogger("OntoRais");
...
/**
* Add a new Stakeholder Type
*
* #param stakeholdertype The new Stakeholder to be added.
* #return
*/
#ApiOperation(value = "Add new stakeholder type",
notes = "",
response = ResponseResource.class,
responseContainer = "Object")
#JsonView(Views.Details.class)
#RequestMapping(value = "/api/stakeholder/types", method = RequestMethod.POST)
public ResponseEntity<List<StakeholderType>> addStakeholderType(#RequestBody StakeholderType stakeholdertype) {
logger.info("Add Stakeholder type in controller");
getStakeholderService().createStakeholderType(stakeholdertype);
return getStakeholderTypes();
}
When calling api/stakeholder/types" with method = POST
This is my debug output:
Add Stakeholder type in controller
Create stakeholder type in service layer
INSERT INTO prefix_http_www_ontorais_de_stakeholdertype(id,stakeholderName) VALUES(DEFAULT,'TESTEWRTERETE');
As you can see the log from hasPermission is not present -> not called.
I can see that the method is called from my other method sercurity annotations in other service objects.
A similar Service which correctly invokes hasPermission as expected just for comparison:
#Service
public class OrganisationService {
private static final Logger logger = LoggerFactory.getLogger("OntoRais");
#Autowired
private OntopManager om;
#Autowired
private ProjectService projectService;
...
#PreAuthorize("hasAuthority('Add_Organisation')")
public void addOrganisation(Organisation organisation) {
List<OBDADataSource> sources = om.getObdaModel().getSources();
OBDAMappingAxiom mapping = null;
try {
mapping = om.getObdaModel().getMapping(new URI("genertatedURI"), MappingList.OrganisationMapping());
} catch (URISyntaxException e) {
e.printStackTrace();
}
HashMap<String, String> valueMap = new HashMap<>();
valueMap.put("organisationName", organisation.getName());
valueMap.put("organisationDescription", organisation.getDescription());
String query = ClassSQLHelper.generateSQLCreateSatement(mapping.getSourceQuery(), valueMap);
SQLHelper.executeSQL(query, sources.get(0));
}
Any hints on what I am doing wrong/missing/am blind for are very welcome thanks.
Benedict
Ok I found the problem, and a solution.
The Problem was that my CustomPermissionEvaluator depended on a method within the StakeholderService. Even though that method was not secured this resulted in the Spring not being able to proxy the object, therefore preventing any security checks.
Even though it is a bad idea to use a service layer object in the PermissionEvaluator, perhaps someone could elaborate on the exact implications, as i am definately not an expert in spring sercurity
Related
I have a security jar that I am implementing in my project. I am extending the BasicSecurityConfig which extends WebSecurityConfigurerAdapter and has all the filters. I was told that all I need is to extend the BasicSecurityConfig and call super().configure which will call the parent's configure method. However, I am getting this error,
Field authenticationManager in com.custom.security.CustomSecurityFilter required a bean of type 'org.springframework.security.authentication.AuthenticationManager' that could not be found.
The parent class already has an AuthenticationManager bean, I shouldn't need to define it in the child class too.
My Security Class
public class SecurityConfiguration extends BasicSecurityConfig {
private static final String PAYMONEYROLE = "(hasRole('EE'))";
#Override
protected void configure(HttpSecurity http) throws Exception {
// must call super first
super.configure(http);
http.authorizeRequests()
.antMatchers(HttpMethod.POST, "/v1/cart/validate").authenticated()
.antMatchers(HttpMethod.POST, "/v1/cart/validate").access(PAYMONEYROLE)
.and().cors().and().csrf().disable();
}
#Bean
public FilterRegistrationBean invalidResourceFilterRegistration(InvalidResourceFilter invalidResourceFilter) {
FilterRegistrationBean registration = new FilterRegistrationBean(invalidResourceFilter);
registration.setEnabled(false);
invalidResourceFilter.setDisabled(true);
return registration;
}
#Bean
public FilterRegistrationBean customSecurityFilterRegistration(CustomSecurityFilter customSecurityFilter) {
FilterRegistrationBean registration = new FilterRegistrationBean(customSecurityFilter);
registration.setEnabled(false);
return registration;
}
}
Custom Security Jar
public class BasicSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private ApplicationContext applicationContext;
#Autowired
private InvalidResourceFilter invalidResourceFilter;
public BasicSecurityConfig() {
}
protected void configure(HttpSecurity http) throws Exception {
((HttpSecurity)((HttpSecurity)http.addFilterBefore(this.customSecurityFilter(), AbstractPreAuthenticatedProcessingFilter.class).addFilterAfter(this.invalidResourceFilter, FilterSecurityInterceptor.class).sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()).exceptionHandling().authenticationEntryPoint(this.authenticationEntryPoint()).accessDeniedHandler(this.customDeniedHandler()).and()).authorizeRequests().accessDecisionManager(this.accessDecisionManager());
}
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(new String[]{"/docs**/**", "/swagger-ui.html**/**", "/webjars**/**", "/swagger-resources**/**", "/api-docs**/**", "/v2/api-docs**", "/version.json**"});
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(this.customAuthenticationProvider());
}
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler() {
return new CustomWebSecurityExpressionHandler();
}
#Bean
public CustomSecurityFilter customSecurityFilter() {
return new CustomSecurityFilter();
}
#Bean
public AuthenticationProvider customAuthenticationProvider() {
return new CustomAuthenticationProvider();
}
#Bean
public AuthenticationSuccessHandler customSuccessHandler() {
return new CustomSuccessHandler();
}
#Bean
public AccessDeniedHandler customDeniedHandler() {
return new CustomAccessDeniedHandler();
}
#Bean
public AuthenticationEntryPoint authenticationEntryPoint() {
return new CustomAuthenticationEntryPoint();
}
#Bean
public CustomSystemUserAuthVoter customSystemUserAuthVoter() {
return new CustomSystemUserAuthVoter();
}
#Bean
public WebExpressionVoter webExpressionVoter() {
WebExpressionVoter wev = new WebExpressionVoter();
wev.setExpressionHandler(this.defaultWebSecurityExpressionHandler());
return wev;
}
#Bean
public AccessDecisionManager accessDecisionManager() {
return new ExplicitDecisionManager(Arrays.asList(this.customSystemUserAuthVoter(), this.webExpressionVoter()));
}
}
As per your error, CustomSecurityFilter is supposed to have authenticationManager injected. But it is created without authenticationManager as below.
#Bean
public CustomSecurityFilter customSecurityFilter() {
return new CustomSecurityFilter();
}
If you can modify BasicSecurityConfig, then update the method as follows:
#Bean
public CustomSecurityFilter customSecurityFilter() {
//If it has a constructor which accept AuthenticationManager
return new CustomSecurityFilter(authenticationManagerBean());
//If it has a setter for AuthenticationManager instead
CustomSecurityFilter filter = new CustomSecurityFilter();
filter.setAuthenticationManager(authenticationManagerBean())
return filter;
}
If you cannot modify BasicSecurityConfig, then create it in SecurityConfiguration with Primary
#Bean
#Primary
public CustomSecurityFilter customSecurityFilter() {
//If it has a constructor which accept AuthenticationManager
return new CustomSecurityFilter(authenticationManagerBean());
//If it has a setter for AuthenticationManager instead
CustomSecurityFilter filter = new CustomSecurityFilter();
filter.setAuthenticationManager(authenticationManagerBean())
return filter;
}
I already found questions with answers but they don't help me.
I have a web servlet project where I use a Spring Controller (4.2.5) and Spring Security (4.0.2). I don't use Spring Boot.
My project works fine.
But now my task is this:
Make #RequestMapping(value={"auth/**"} configurable (replace "auth/**" with ${dm.filterPattern})
Problem: in #RequestMapping ${dm.filterPattern} isn't resolved, although #PropertySource is processed.
This is the entry dm.filterPattern in dmConfig.properties:
dm.filterPattern=/auth/*
Here is some essential code, with all Spring annotations.
Controller:
The output of the method init() shows me that #PropertySource is processed correctly. env.getProperty("...") returns correct values.
#Controller
#PropertySource("classpath:/dmConfig.properties")
#RequestMapping(value ={ "${dm.filterPattern}"})
public class DmProxyController implements ApplicationContextAware
{
private Environment env;
#Autowired
public DmProxyController(Environment env)
{
this.env = env;
}
#RequestMapping(path={"${dm.filterPattern}"} ,method = RequestMethod.POST)
protected void doPost(HttpServletRequest customerRequest, HttpServletResponse response)
throws ServletException, IOException, DmException
{
// code for POST request
}
#RequestMapping(path={"${dm.filterPattern}"} ,method = RequestMethod.GET)
protected void doGet(HttpServletRequest customerRequest, HttpServletResponse response)
throws ServletException, IOException, DmException
{
// code for GET request
}
#PostConstruct
public void init() throws ServletException
{
RequestMappingHandlerMapping requestMapping=
(RequestMappingHandlerMapping) appContext.getBean("requestMappingHandlerMapping");
Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMapping.getHandlerMethods();
logger.debug("RequestMapping via dm.filterPattern: {}",
env.getProperty("dm.filterPattern"));
logger.debug("Handler Methods: {}", handlerMethods.size());
for (RequestMappingInfo mapInfo : handlerMethods.keySet())
{
logger.debug(" Mappinginfo: {} --> {}", mapInfo, handlerMethods.get(mapInfo));
}
}
}
Class with bean definitions
#Configuration
#PropertySource("classpath:/dmConfig.properties")
#ComponentScan(basePackages = "com.dm.filter, com.dm.controller")
#EnableTransactionManagement(mode = AdviceMode.PROXY, proxyTargetClass = false)
#Import({DmSecurityConfigurer.class, DmWebConfigurer.class})
public class DmRoot
{
}
DispatcherServletInitializer
public class DmDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
{
#Override
protected Class<?>[] getRootConfigClasses()
{ return new Class[]{DmRoot.class}; }
#Override
protected Class<?>[] getServletConfigClasses()
{ return null; }
#Override
protected String[] getServletMappings()
{ return new String[]{"/"}; }
#Override
protected String getServletName()
{ return "dmDispatcherServlet"; }
#Override
protected void customizeRegistration(ServletRegistration.Dynamic registration)
{
super.customizeRegistration(registration);
registration.setLoadOnStartup(1);
}
}
WebConfigurer
public class DmWebConfigurer extends WebMvcConfigurerAdapter
{
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
super.addResourceHandlers(registry);
registry.addResourceHandler("/index.html").addResourceLocations("/");
registry.setOrder(Integer.MAX_VALUE-5);
}
}
SecurityWebApplicationInitializer
public class DmSecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer
{
public DmSecurityWebApplicationInitializer()
{
// some logging
}
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext)
{ // adding own filters }
#Override
protected void afterSpringSecurityFilterChain(ServletContext servletContext)
{ // adding own filters }
}
SecurityConfigurer
#EnableWebMvc
#EnableWebSecurity
#PropertySource("classpath:dmConfig.properties")
public class DmSecurityConfigurer extends WebSecurityConfigurerAdapter
{
private static Logger logger = LogManager.getLogger(DmSecurityConfigurer.class.getName());
#Autowired
private Environment env;
#Autowired
private UserDetailsService dmUserDetailsService;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception
{
String urlPattern = env.getProperty("dm.springSecurityPattern");
String realmName = env.getProperty("dm.springSecurityRealm");
httpSecurity.httpBasic().realmName(realmName)
.and().userDetailsService(dmUserDetailsService)
.authorizeRequests()
.antMatchers(urlPattern).authenticated()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable();
}
}
There is a possibility that PropertySourcesPlaceholderConfigurer is being initialised later in the spring context than your controller and hence the values are not resolved. try adding explicit bean definition for PropertySourcesPlaceholderConfigurer in one of the root configuration file as below;
#PropertySource("classpath:/dmConfig.properties")
public class DmWebConfigurer extends WebMvcConfigurerAdapter
{
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
super.addResourceHandlers(registry);
registry.addResourceHandler("/index.html").addResourceLocations("/");
registry.setOrder(Integer.MAX_VALUE-5);
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
The reason you can see the values properly in your init() method is because it is called after all the beans are initialised including PropertySourcesPlaceholderConfigurer.
I have a Spring Boot project in which I've configured a Spring OAuth2 authentication process which partially works. I can authenticate OK but when I'm trying to get a refresh token I get an exception.
OAuth configuration:
#Configuration
public class OAuth2ServerConfiguration {
private static final String RESOURCE_ID = "xxx";
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**").authenticated();
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Value("${clientDetailsService.clientName}")
private String clientName;
#Value("${clientDetailsService.clientSecret}")
private String clientSecret;
#Autowired
#Qualifier("authenticationManager")
private AuthenticationManager authenticationManager;
#Autowired
private ClientDetailsService clientDetailsService;
#Autowired
#Qualifier("tokenServices")
private AuthorizationServerTokenServices tokenServices;
#Autowired
#Qualifier("codeServices")
private AuthorizationCodeServices codeServices;
#Autowired
#Qualifier("requestFactory")
private OAuth2RequestFactory requestFactory;
#Autowired
#Qualifier("tokenGranter")
private TokenGranter tokenGranter;
private final TokenStore tokenStore = new InMemoryTokenStore();
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.setClientDetailsService(clientDetailsService);
endpoints.tokenServices(tokenServices)
.tokenStore(tokenStore)
.authorizationCodeServices(codeServices)
.authenticationManager(authenticationManager)
.requestFactory(requestFactory)
.tokenGranter(tokenGranter);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
#Bean(name = "tokenGranter")
#Primary
public TokenGranter tokenGranter() {
final List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>();
tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, codeServices, clientDetailsService, requestFactory));
tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetailsService, requestFactory));
tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetailsService, requestFactory));
tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetailsService, requestFactory));
tokenGranters.add(new CustomTokenGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory));
return new CompositeTokenGranter(tokenGranters);
}
#Bean
#Primary
public ClientDetailsService clientDetailsService(){
final InMemoryClientDetailsServiceBuilder builder = new InMemoryClientDetailsServiceBuilder();
builder.withClient(clientName)
.authorizedGrantTypes("password", "refresh_token")
.authorities("USER")
.scopes("read", "write")
.resourceIds(RESOURCE_ID)
.secret(clientSecret);
try {
return builder.build();
} catch (final Exception e) {
e.printStackTrace();
}
return null;
}
#Bean(name = "tokenServices")
#Primary
public AuthorizationServerTokenServices tokenServices() {
final DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setClientDetailsService(clientDetailsService);
tokenServices.setTokenStore(tokenStore);
tokenServices.setAuthenticationManager(authenticationManager);
return tokenServices;
}
#Bean(name = "requestFactory")
#Primary
public OAuth2RequestFactory requestFactory() {
return new DefaultOAuth2RequestFactory(clientDetailsService);
}
#Bean(name = "codeServices")
#Primary
public AuthorizationCodeServices authorizationCodeServices() {
return new InMemoryAuthorizationCodeServices();
}
}
I also have some custom components defined, like a custom Token Granter, custom authentication provider etc. I'll post them if necessary.
As I said, authentication flow works OK. When I POST to /oauth/token I get a token and a refresh token, but when I then try to exchange my refresh token for a new token (by POSTing http://localhost:8080/oauth/token with grant_type=refresh_token and refresh_token=my refresh token) I get an exception:
No AuthenticationProvider found for org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
Where do I set the authentication provider? How do I get Spring to use my custom authentication provider for refresh tokens also?
I fixed this by explicitly defining a PreAuthenticationProvider:
#Component("preAuthProvider")
public class CustomPreAuthProvider extends PreAuthenticatedAuthenticationProvider {
#Autowired
private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> userService;
public CustomPreAuthProvider(){
super();
}
#PostConstruct
public void init(){
super.setPreAuthenticatedUserDetailsService(userService);
}
}
and then a custom userservice:
#Service
public class CustomPreAuthUserDetailsService implements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {
#Override
public final UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken token) {
...
}
}
and adding this provider to the oauth2 config:
#Autowired
private AuthenticationProvider authenticationProvider;
#Autowired
#Qualifier("preAuthProvider")
private AuthenticationProvider preAuthProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider).authenticationProvider(preAuthProvider);
}
As an alternative way to Jesper's answer, if you want to reuse your current UserDetailsService for this purpose, you can just do it the same way as Spring does it with their DefaultTokenServices:
#Bean
public CustomTokenServices tokenServices() {
CustomTokenServices tokenServices = new CustomTokenServices();
tokenServices.setTokenStore(tokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setReuseRefreshToken(false);
tokenServices.setClientDetailsService(clientDetailsService);
tokenServices.setAuthenticationManager(createPreAuthProvider());
return tokenServices;
}
private ProviderManager createPreAuthProvider() {
PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<>(userDetailsService));
return new ProviderManager(Arrays.asList(provider));
}
UserDetailsService is autowired into this #Configuration class.
I can not be disagree with Jesper's answer, but in my case, that same error has been fixed removing:
tokenServices.setAuthenticationManager(authenticationManager)
from tokenService()
This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 7 years ago.
I want to make a check in database when i receive a request. So i did a Interceptor like below,
CustomInterceptor.java
#Component
public class CustomInterceptor extends HandlerInterceptorAdapter {
#Autowired
private DatabaseService databaseService;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//Set Request Attribute(TODO)
LogService.info(this.getClass().getName(), "New Request URI is:" + request.getRequestURI());
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
String authToken = request.getHeader("AuthToken");
boolean isValidRequest = databaseService.checkIfTokenIsValid(authToken);
}
}
Application.class:
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
// protected Properties props = new Properties();
//
// public Application() {
// props.setProperty("error.path", "/error");
//// props.setProperty("error.whitelabel.enabled", "false");
//// props.setProperty("org.springframework.web", "DEBUG");
// }
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
// application.properties(props);
return application.sources(Application.class);
}
#Override
public void onStartup(final ServletContext servletContext) throws ServletException {
LogService.info(Application.class.getName(), "Loading Service...");
super.onStartup(servletContext);
LogService.info(Application.class.getName(), "Service Started");
}
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Application.class, args);
}
DatabasService.java
#Service
public class DatabaseService {
#Autowired
private ApplicationProperties properties;
private final JdbcTemplate defaultJdbcTemplate;
#Autowired
public DatabaseService(
#Qualifier("dataSource") DataSource dataSource) {
defaultJdbcTemplate = new JdbcTemplate(dataSource);
}
public boolean checkIfTokenIsValid() {
//Perform Check
}
}
CustomWebConfiguration.java
#Configuration
#EnableWebMvc
public class CustomWebConfiguration extends WebMvcConfigurerAdapter {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/"};
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!registry.hasMappingForPattern("/**")) {
registry.addResourceHandler("/**").addResourceLocations(
CLASSPATH_RESOURCE_LOCATIONS);
}
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CustomInterceptor())
.addPathPatterns("/**");
}
}
But i get NullPointer At: boolean isValidRequest = databaseService.checkIfTokenIsValid(authToken);
What is wrong here, why cannot spring Autowire the Databaseservice in Interceptor?
Note: Autowire works fine everywhere else, but not in the interceptor.
Solution (Thanks to M. Deinum)
Change the CustomWebConfiguration.java like below;
#Configuration
#EnableWebMvc
public class CustomWebConfiguration extends WebMvcConfigurerAdapter {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/"};
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!registry.hasMappingForPattern("/**")) {
registry.addResourceHandler("/**").addResourceLocations(
CLASSPATH_RESOURCE_LOCATIONS);
}
}
#Bean
public CustomInterceptor customInterceptor() {
return new CustomInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(customInterceptor())
.addPathPatterns("/**");
}
}
Spring will only autowire beans it knows about, you are creating the instance yourself outside the control of Spring.
Either inject a CustomInterceptor into your configuration class or add a #Bean method to make it a Spring managed instance. Then use that instance to add it to the list of interceptors.
It is OK to use #SpringBootApplication on your main class, however don't you miss #EnableAutoConfiguration and #ComponentScan on the same class to tell Spring Boot to look for other components and services automatically?
I'm facing some issues when I run and hit my rest service, I saw that my service instance got a #Proxy value. Below you can see the classes that I have:
Initializer class:
public class AllureWebInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
createDispatcherServlet(servletContext);
initializeListener(servletContext);
}
private void initializeListener(ServletContext servletContext) {
AnnotationConfigWebApplicationContext rootContext = createContext();
servletContext.addListener(new ContextLoaderListener(rootContext));
}
private void createDispatcherServlet(final ServletContext context) {
AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext();
dispatcherServlet.register(AllureWebConfig.class);
ServletRegistration.Dynamic dispatcher = context.addServlet(AllureWebConstants.DISPATCHER, new DispatcherServlet(dispatcherServlet));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping(AllureWebConstants.SLASH);
}
private AnnotationConfigWebApplicationContext createContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(AllureWebConfig.class);
return context;
}
}
Config class:
#Configuration
#Import(AllureServiceConfig.class)
#ComponentScan(basePackages = {"com.allure.events.web.controller"})
#EnableWebMvc
public class AllureWebConfig extends WebMvcConfigurerAdapter {
#Autowired
private ApplicationContextProvider applicationContextProvider;
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(AllureWebConstants.RESOURCES_DOUBLE_WILDCARD)
.addResourceLocations(AllureWebConstants.RESOURCES);
}
#Bean
public ApplicationContextProvider applicationContextProvider() {
return applicationContextProvider;
}
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource;
messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:META-INF/web/resources/release");
messageSource.setUseCodeAsDefaultMessage(true);
return messageSource;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(Include.NON_NULL);
converter.setObjectMapper(objectMapper);
converters.add(converter);
super.configureMessageConverters(converters);
}
}
Controller class:
#Controller
public class SupplierEndpoint extends AllureEndpoint {
#Autowired
SupplierService supplierService;
#RequestMapping(value = AllureWebConstants.SUPPLIERS, method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public List<Supplier> getSuppliers() {
List<Supplier> suppliers = getSuppliersList(supplierService.getSuppliers());
return suppliers;
}
}
Service interface
public interface SupplierService {
public List<SupplierDto> getSuppliers();
}
Service Impl
#Service
public class SupplierServiceImpl implements SupplierService {
#Autowired
private SupplierMapper supplierMapper;
#Autowired
private SupplierDtoHelper supplierHelper;
#Override
#Transactional
public List<SupplierDto> getSuppliers() {
List<Supplier> suppliers = supplierMapper.getSuppliers();
List<SupplierDto> dtos = new ArrayList<SupplierDto>();
for (Supplier supplier : suppliers) {
dtos.add(supplierHelper.convertEntityToDto(supplier));
}
return dtos;
}
}
if I debug, I see that the instance value is :
supplierService - $Proxy28
|
-> h = JdkDynamicAopProxy
Can someone guide me, please?
Regards,
Edgardo Quiroz
It's not an issue when you work with Spring.
Spring Framework uses Proxy to make the different features possible.
Here you have a Transactional Annotation and proxy is been used to implement it.