Spring Security的@PreAuthorize导致415“不支持的媒体类型”错误

时间:2017-05-11 17:00:26

标签: spring-security

我在我的应用程序中通过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”是我问题的充分解决方案,但问题仍然是为什么这是一个问题。

0 个答案:

没有答案