---
title: Spring boot security @PreAuthorize导致Controller无法启动问题
date: 2017-12-12 22:18:53
tags: ['spring boot', 'spring boot security', 'solution']
---
最近在考虑增强系统的安全性,主要使用 spring boot security & spring boot oauth2 来保证系统的安全。
# 主要依赖(Dependence)
```xml
org.springframework.boot
spring-boot-starter-security
org.springframework.cloud
spring-cloud-starter-oauth2
```
## 全局依赖配置(GlobalAuthenticationConfigurerAdapter)
```java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableGlobalAuthentication
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class GlobalSecurityConfig extends GlobalAuthenticationConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
```
## 用户控制器(UserController)
```java
package cn.edu.cqjtu.oj.web.v1;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.annotation.JsonView;
import cn.edu.cqjtu.oj.domain.TbUser;
import cn.edu.cqjtu.oj.domain.TbUser.UserSimpleView;
import cn.edu.cqjtu.oj.service.UserService;
import cn.edu.cqjtu.oj.support.OAuth2User;
import cn.edu.cqjtu.oj.support.UserQueryCondition;
import io.swagger.annotations.ApiOperation;
/**
* 用户控制器
*
* @author johnniang
*
*/
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private UserService userService;
// @Autowired
// private MyValidator validator;
@GetMapping
@ApiOperation("分页查询用户")
@JsonView(UserSimpleView.class)
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
public List list(UserQueryCondition condition) {
// 查询符合条件的用户
List users = userService.query(condition);
return users;
}
@GetMapping("/me")
@ApiOperation("查询用户详情")
@JsonView(UserSimpleView.class)
public OAuth2User getDetails(Authentication authentication) {
if (logger.isDebugEnabled()) {
logger.debug("Principal class: {}", authentication.getClass());
}
return (OAuth2User) authentication.getPrincipal();
}
}
```
## 遇到的问题(Problem)
在 UserController 中一旦加入了`@PreAuthorize("hasAuthority('ROLE_ADMIN')")`注解,将会导致 Spring 扫描不到 UserController,最终没办法访问此 URL,所以将会出现 404 错误。
## 解决方案(Solution)
搞了两天了,已经无计可施了。各种 Google / Baidu,没有任何一个解决方案适合这个问题。
```java
package cn.edu.cqjtu.oj.web;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.edu.cqjtu.oj.web.v1.UserController;
@RestController
@RequestMapping("/test")
public class TestController {
Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private UserController userController;
@GetMapping
public void testUserController() {
if (logger.isDebugEnabled()) {
logger.debug("User Controller: {}", userController);
}
}
}
```
最后自己写了一个 TestController,就直接用使用 Logger 打印了一下 userController 这个对象。问题终于浮出水面:
```text
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'userController' could not be injected as a 'cn.edu.cqjtu.oj.web.v1.UserController' because it is a JDK dynamic proxy that implements:
Action:
Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies by setting proxyTargetClass=true on @EnableAsync and/or @EnableCaching.
```
这不就是使用了 JDK 自身的代理么,导致没办法使用 AOP 进行监控方法的执行权限。
最后一根救命稻草终于结束了这么久这么长久的搜寻工作。
```yml
spring:
aop:
proxy-target-class: true
```
Spring boot security @PreAuthorize导致Controller无法启动问题