【发布时间】:2016-10-13 14:14:21
【问题描述】:
我正在尝试使用 @PreAuthorize 实现方法安全性。
春季版:4.2.3.Release Spring Security:4.0.3.Release
我已经实现了一个 CustomPermissionEvaluator。我注意到它似乎工作正常,除了没有调用 hasPmerission 的 1 个服务。 我知道这一点是因为我从 hasPermission 获得了一条日志消息 / 或者在错误的情况下没有获得日志:
public boolean hasPermission(Authentication authentication, Object o, Object o1) {
logger.info("Call to hasPermission with "+o+" and "+o1);
...
}
我的Spring配置如下:
@Configuration
@ComponentScan
public class RootConfiguration {
}
MVC 配置
@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;
}
}
方法安全配置:
@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;
}
}
初始化器:
@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")
};
}
}
安全配置:
@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;
}
相关服务:
@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());
}
}
以及我从中调用服务层的控制器:
@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();
}
使用方法 = POST 调用 api/stakeholder/types 时 这是我的调试输出:
Add Stakeholder type in controller
Create stakeholder type in service layer
INSERT INTO prefix_http_www_ontorais_de_stakeholdertype(id,stakeholderName) VALUES(DEFAULT,'TESTEWRTERETE');
如您所见,hasPermission 的日志不存在 -> 未调用。 我可以看到该方法是从其他服务对象中的其他方法安全注释中调用的。
一个类似的服务,它按预期正确调用了 hasPermission,只是为了比较:
@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));
}
非常欢迎任何关于我做错/失踪/失明的提示。
本尼迪克特
【问题讨论】:
-
OntoRais.*不是有效的包名称。StakeholderService没有实现接口,所以为了让@PreAuthorize工作,必须包含像 cglib 或 javassist 这样的库。你们的服务有什么区别? -
好的,将我的组件扫描更改为
@ComponentScan({"OntoRais.api", "OntoRais.datalayer.database.service", "OntoRais.security", "OntoRais.datalayer.ontology.ontop", "OntoRais.datalayer.ontology.service", "OntoRais.config" })。没有 ogf 我的服务实现了一个接口。我的所有服务不应该出现问题吗?我在服务中找不到任何区别。为了确定,我将在原始问题中包含一项工作服务。 -
如果没有服务实现接口,那么问题当然是别的。我认为
@ComponentScan({"OntoRais"})应该足够了。在StakeholderController中,getStakeholderService()的代码是什么?如果它不返回安全代理,那就是问题所在。您可以在服务方法中添加断点并查看调用堆栈。 -
在
getStakeholderService()中,我只返回@Autowired 服务的本地实例 -
我在几个不同的地方读到过,你把
@EnableGlobalMethodSecurity(prePostEnabled = true)放在哪里很重要这可能是这种行为的原因吗?
标签: java spring spring-mvc spring-security