我在我的应用程序中通过Java Config使用Spring Boot,Spring Data JPA,Spring Data REST和Spring Security。我正在尝试将此@PreAuthorize注释添加到UserPrefsRESTController中的以下方法。
接口
package com.company.services.rest;
import com.company.server.model.user.preferences.UserPrefs;
import com.company.server.user.prefs.UserPrefsDTO;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@RequestMapping("/api/userprefs")
public interface UserPrefsRESTController {
@RequestMapping(method = RequestMethod.POST, produces = "application/json")
@PreAuthorize("#userPrefs.id == principal.username or hasRole('ADMIN')")
ResponseEntity<UserPrefsDTO> setUserPrefs(@RequestBody UserPrefs userPrefs);
@RequestMapping(method = RequestMethod.GET, produces = "application/json")
ResponseEntity<UserPrefsDTO> getUserPrefs(@RequestParam String id);
}
实施班级:
package com.company.services.rest;
import com.company.server.model.user.preferences.UserPrefs;
import com.company.server.user.prefs.UserPrefsDTO;
import com.company.server.user.prefs.UserPrefsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserPrefsRESTControllerImpl implements UserPrefsRESTController {
@Autowired
private UserPrefsService userPrefsService;
@Override
public ResponseEntity<UserPrefsDTO> setUserPrefs(UserPrefs userPrefs) {
UserPrefsDTO response;
try {
response = userPrefsService.setUserPrefsValue(userPrefs);
} catch (final IllegalArgumentException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
return ResponseEntity.ok(response);
}
@Override
public ResponseEntity<UserPrefsDTO> getUserPrefs(String id) {
return ResponseEntity.ok(userPrefsService.getUserPrefs(id));
}
}
当注释不存在时,端点正确返回UserPrefsDTO,但是当我添加注释时,我得到以下错误
{
"timestamp": 1494518855146,
"status": 415,
"error": "Unsupported Media Type",
"exception": "org.springframework.web.HttpMediaTypeNotSupportedException",
"message": "Unsupported Media Type",
"path": "/api/userprefs"
}
我也尝试将注释更改为@PostAuthorize并且我得到了相同的错误,并且没有在方法内点击我的断点,因此它与注释在那里而不是具体为@PreAuthorize这一事实有关。我还尝试了其他几个表达式,包括角色检查和permitAll(),并获得相同的结果。无论经过身份验证的用户是管理员,相关用户(匹配#userPrefs.id == principal.username
)还是其他用户,所有经过身份验证的请求都会返回错误。我也尝试从@RequestMapping中删除produces = "application/json"
;并添加consumes = "application/json"
。两者都无济于事
这是其他相关代码
UserPrefsDTO:
package com.company.server.user.prefs;
import java.io.Serializable;
public class UserPrefsDTO implements Serializable{
private String id;
private String value;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
配置:
package com.company.spring.boot;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("runuser").password("password").roles("USER", "RUN").and()
.withUser("admin").password("password").roles("USER", "RUN", "ADMIN");
}
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().permitAll().and()
.logout().permitAll().and()
.authorizeRequests()
.anyRequest().authenticated()
.and().csrf().disable()
.httpBasic();
}
}
UserPrefsRepository:
package com.company.server.model.repositories;
import com.company.server.model.ModelConstants;
import com.company.server.model.user.preferences.UserPrefs;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
import javax.persistence.PersistenceContext;
@PersistenceContext(name = ModelConstants.DOMAIN_ID)
@RepositoryRestResource(collectionResourceRel = "UserPrefs", path = "UserPrefs", itemResourceRel="UserPrefs")
public interface UserPrefsRepository extends JpaRepository<UserPrefs, String> {
@Override
@PreAuthorize("hasRole('ADMIN')")
Page<UserPrefs> findAll(Pageable pageable);
@PostAuthorize("returnObject != null && returnObject.id == principal.username or hasRole('ADMIN')")
UserPrefs findById(@Param("id") String id);
}
奇怪的是,我在一个事件处理程序中成功使用相同的注释,我用它来捕获对UserPrefs对象的Spring Data REST生成的端点的请求
UserPrefsEventHandler:
package com.company.server.model.user.preferences;
import org.springframework.data.rest.core.annotation.HandleBeforeCreate;
import org.springframework.data.rest.core.annotation.HandleBeforeDelete;
import org.springframework.data.rest.core.annotation.HandleBeforeSave;
import org.springframework.data.rest.core.annotation.RepositoryEventHandler;
import org.springframework.security.access.prepost.PreAuthorize;
@RepositoryEventHandler
@SuppressWarnings("unused")
public class UserPrefsEventHandler {
@HandleBeforeCreate
@PreAuthorize("#userPrefs.id == principal.username or hasRole('ADMIN')")
public void checkCreateAuthority(UserPrefs userPrefs) {
System.out.println("checkCreateAuthority");
}
@HandleBeforeSave
@PreAuthorize("#userPrefs.id == principal.username or hasRole('ADMIN')")
public void checkUpdateAuthority(UserPrefs userPrefs) {
System.out.println("checkUpdateAuthority");
}
@HandleBeforeDelete
@PreAuthorize("hasRole('ADMIN')")
public void checkDeleteAuthority(UserPrefs userPrefs) {
System.out.println("checkDeleteAuthority");
}
}
注意:上面的类是通过另一个配置文件中的bean添加的:
@Bean
UserPrefsEventHandler userPrefsEventHandler() {
return new UserPrefsEventHandler();
}
导致415错误的原因是什么?
更新 - Spring Security调试日志:
2017-05-11 11:51:18.784 DEBUG 10256 --- [qtp424848797-87] o.s.security.web.FilterChainProxy : /api/userprefs at position 1 of 13 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2017-05-11 11:51:18.786 DEBUG 10256 --- [qtp424848797-87] o.s.security.web.FilterChainProxy : /api/userprefs at position 2 of 13 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2017-05-11 11:51:18.787 DEBUG 10256 --- [qtp424848797-87] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2017-05-11 11:51:18.787 DEBUG 10256 --- [qtp424848797-87] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
2017-05-11 11:51:18.790 DEBUG 10256 --- [qtp424848797-87] o.s.security.web.FilterChainProxy : /api/userprefs at position 3 of 13 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2017-05-11 11:51:18.790 DEBUG 10256 --- [qtp424848797-87] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@27192c6
2017-05-11 11:51:18.790 DEBUG 10256 --- [qtp424848797-87] o.s.security.web.FilterChainProxy : /api/userprefs at position 4 of 13 in additional filter chain; firing Filter: 'LogoutFilter'
2017-05-11 11:51:18.790 DEBUG 10256 --- [qtp424848797-87] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', GET]
2017-05-11 11:51:18.791 DEBUG 10256 --- [qtp424848797-87] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'POST /api/userprefs' doesn't match 'GET /logout
2017-05-11 11:51:18.791 DEBUG 10256 --- [qtp424848797-87] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', POST]
2017-05-11 11:51:18.791 DEBUG 10256 --- [qtp424848797-87] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/api/userprefs'; against '/logout'
2017-05-11 11:51:18.791 DEBUG 10256 --- [qtp424848797-87] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', PUT]
2017-05-11 11:51:18.791 DEBUG 10256 --- [qtp424848797-87] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'POST /api/userprefs' doesn't match 'PUT /logout
2017-05-11 11:51:18.791 DEBUG 10256 --- [qtp424848797-87] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', DELETE]
2017-05-11 11:51:18.791 DEBUG 10256 --- [qtp424848797-87] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'POST /api/userprefs' doesn't match 'DELETE /logout
2017-05-11 11:51:18.791 DEBUG 10256 --- [qtp424848797-87] o.s.s.web.util.matcher.OrRequestMatcher : No matches found
2017-05-11 11:51:18.791 DEBUG 10256 --- [qtp424848797-87] o.s.security.web.FilterChainProxy : /api/userprefs at position 5 of 13 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
2017-05-11 11:51:18.792 DEBUG 10256 --- [qtp424848797-87] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/api/userprefs'; against '/login'
2017-05-11 11:51:18.792 DEBUG 10256 --- [qtp424848797-87] o.s.security.web.FilterChainProxy : /api/userprefs at position 6 of 13 in additional filter chain; firing Filter: 'DefaultLoginPageGeneratingFilter'
2017-05-11 11:51:18.792 DEBUG 10256 --- [qtp424848797-87] o.s.security.web.FilterChainProxy : /api/userprefs at position 7 of 13 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
2017-05-11 11:51:18.794 DEBUG 10256 --- [qtp424848797-87] o.s.s.w.a.www.BasicAuthenticationFilter : Basic Authentication Authorization header found for user 'admin'
2017-05-11 11:51:18.795 DEBUG 10256 --- [qtp424848797-87] o.s.s.authentication.ProviderManager : Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
2017-05-11 11:51:18.802 DEBUG 10256 --- [qtp424848797-87] o.s.s.w.a.www.BasicAuthenticationFilter : Authentication success: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@e0123f1f: Principal: org.springframework.security.core.userdetails.User@586034f: Username: admin; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_RUN,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_RUN, ROLE_USER
2017-05-11 11:51:18.802 DEBUG 10256 --- [qtp424848797-87] o.s.security.web.FilterChainProxy : /api/userprefs at position 8 of 13 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2017-05-11 11:51:18.802 DEBUG 10256 --- [qtp424848797-87] o.s.security.web.FilterChainProxy : /api/userprefs at position 9 of 13 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2017-05-11 11:51:18.804 DEBUG 10256 --- [qtp424848797-87] o.s.security.web.FilterChainProxy : /api/userprefs at position 10 of 13 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2017-05-11 11:51:18.804 DEBUG 10256 --- [qtp424848797-87] o.s.s.w.a.AnonymousAuthenticationFilter : SecurityContextHolder not populated with anonymous token, as it already contained: 'org.springframework.security.authentication.UsernamePasswordAuthenticationToken@e0123f1f: Principal: org.springframework.security.core.userdetails.User@586034f: Username: admin; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_RUN,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_RUN, ROLE_USER'
2017-05-11 11:51:18.804 DEBUG 10256 --- [qtp424848797-87] o.s.security.web.FilterChainProxy : /api/userprefs at position 11 of 13 in additional filter chain; firing Filter: 'SessionManagementFilter'
2017-05-11 11:51:18.804 DEBUG 10256 --- [qtp424848797-87] s.CompositeSessionAuthenticationStrategy : Delegating to org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy@419074cb
2017-05-11 11:51:18.806 DEBUG 10256 --- [qtp424848797-87] w.c.HttpSessionSecurityContextRepository : HttpSession being created as SecurityContext is non-default
2017-05-11 11:51:18.810 DEBUG 10256 --- [qtp424848797-87] w.c.HttpSessionSecurityContextRepository : SecurityContext 'org.springframework.security.core.context.SecurityContextImpl@e0123f1f: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@e0123f1f: Principal: org.springframework.security.core.userdetails.User@586034f: Username: admin; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_RUN,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_RUN, ROLE_USER' stored to HttpSession: 'org.eclipse.jetty.server.session.Session@6c5d0309
2017-05-11 11:51:18.811 DEBUG 10256 --- [qtp424848797-87] o.s.security.web.FilterChainProxy : /api/userprefs at position 12 of 13 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2017-05-11 11:51:18.811 DEBUG 10256 --- [qtp424848797-87] o.s.security.web.FilterChainProxy : /api/userprefs at position 13 of 13 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2017-05-11 11:51:18.811 DEBUG 10256 --- [qtp424848797-87] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', GET]
2017-05-11 11:51:18.811 DEBUG 10256 --- [qtp424848797-87] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'POST /api/userprefs' doesn't match 'GET /logout
2017-05-11 11:51:18.812 DEBUG 10256 --- [qtp424848797-87] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', POST]
2017-05-11 11:51:18.812 DEBUG 10256 --- [qtp424848797-87] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/api/userprefs'; against '/logout'
2017-05-11 11:51:18.812 DEBUG 10256 --- [qtp424848797-87] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', PUT]
2017-05-11 11:51:18.812 DEBUG 10256 --- [qtp424848797-87] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'POST /api/userprefs' doesn't match 'PUT /logout
2017-05-11 11:51:18.812 DEBUG 10256 --- [qtp424848797-87] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', DELETE]
2017-05-11 11:51:18.812 DEBUG 10256 --- [qtp424848797-87] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'POST /api/userprefs' doesn't match 'DELETE /logout
2017-05-11 11:51:18.812 DEBUG 10256 --- [qtp424848797-87] o.s.s.web.util.matcher.OrRequestMatcher : No matches found
2017-05-11 11:51:18.812 DEBUG 10256 --- [qtp424848797-87] o.s.s.w.a.i.FilterSecurityInterceptor : Secure object: FilterInvocation: URL: /api/userprefs; Attributes: [authenticated]
2017-05-11 11:51:18.812 DEBUG 10256 --- [qtp424848797-87] o.s.s.w.a.i.FilterSecurityInterceptor : Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@e0123f1f: Principal: org.springframework.security.core.userdetails.User@586034f: Username: admin; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_RUN,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_RUN, ROLE_USER
2017-05-11 11:51:18.818 DEBUG 10256 --- [qtp424848797-87] o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@721d7b2d, returned: 1
2017-05-11 11:51:18.819 DEBUG 10256 --- [qtp424848797-87] o.s.s.w.a.i.FilterSecurityInterceptor : Authorization successful
2017-05-11 11:51:18.819 DEBUG 10256 --- [qtp424848797-87] o.s.s.w.a.i.FilterSecurityInterceptor : RunAsManager did not change Authentication object
2017-05-11 11:51:18.819 DEBUG 10256 --- [qtp424848797-87] o.s.security.web.FilterChainProxy : /api/userprefs reached end of additional filter chain; proceeding with original chain
2017-05-11 11:51:18.868 DEBUG 10256 --- [qtp424848797-87] w.c.HttpSessionSecurityContextRepository : SecurityContext 'org.springframework.security.core.context.SecurityContextImpl@e0123f1f: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@e0123f1f: Principal: org.springframework.security.core.userdetails.User@586034f: Username: admin; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_RUN,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_RUN, ROLE_USER' stored to HttpSession: 'org.eclipse.jetty.server.session.Session@6c5d0309
2017-05-11 11:51:18.916 DEBUG 10256 --- [qtp424848797-87] o.s.s.w.a.ExceptionTranslationFilter : Chain processed normally
2017-05-11 11:51:18.917 DEBUG 10256 --- [qtp424848797-87] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
奇怪的是,我没有看到任何与调试日志中的错误相关的内容,但我可能会忽视某些内容。
更新2 - “multipart / form-data”与“application / json”
我一直在将请求作为“multipart / form-data”提交,这会产生错误。当我将其作为“application / json”发送时,请求成功。当@PreAuthorize注释不存在时,当提交为“multipart / form-data”时,请求工作正常,我仍然感到困惑。虽然发送“application / json”是我问题的充分解决方案,但问题仍然是为什么这是一个问题。