【发布时间】:2020-04-26 15:12:15
【问题描述】:
我对 Spring Security 和 OAuth2 还是很陌生。作为学习的一部分,我正在尝试设置 OAuth2 授权服务器并保护 REST 端点免受未经授权的访问。
我的资源服务器包含几个端点,具有以下授权。
/products : only user with Authority='ROLE_PRODUCT_USER' and scope='read' can access this endpoint
/addProduct : only user with Authority='ROLE_PRODUCT_ADMIN' and scope='write' can access this endpoint
问题:尝试使用以下方式访问端点时访问被拒绝 邮递员和grant_type="password"
代码
资源服务器
ProductController.java
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@PreAuthorize("#oauth2.hasScope('read') and hasAuthority('ROLE_PRODUCT_USER')")
@GetMapping("/products")
public ResponseEntity<List<Product>> getAllProducts() {
return new ResponseEntity<List<Product>>(productService.getAllProducts(), HttpStatus.OK);
}
@PreAuthorize("#oauth2.hasScope('write') and hasAuthority('ROLE_PRODUCT_ADMIN')")
@PostMapping("/addproduct")
public ResponseEntity<Product> addProduct(@RequestBody Product product) {
return new ResponseEntity<Product>(productService.addProduct(product), HttpStatus.OK);
}
}
资源服务器中的 OAuth 配置
security:
oauth2:
resource:
user-info-uri: http://localhost:9090/user
授权服务器
实现 user-info-uri 的主类
import java.security.Principal;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@EnableAuthorizationServer
@EnableResourceServer
@RestController
public class OAuthAuthorizationServerApplication {
public static void main(String[] args) {
SpringApplication.run(OAuthAuthorizationServerApplication.class, args);
}
@GetMapping("/user")
public Principal user(Principal user) {
System.out.println(user);
return user;
}
}
数据库 oauth_client_details
mysql> select * from oauth_client_details where client_id in ('reader','writer');
+-----------+--------------+----------------------------------------------------------------------+------------+--------------------------------------------------------------+----------------------------+--------------------+-----------------------+------------------------+------------------------+-------------+
| client_id | resource_ids | client_secret | scope | authorized_grant_types | web_server_redirect_uri | authorities | access_token_validity | refresh_token_validity | additional_information | autoapprove |
+-----------+--------------+----------------------------------------------------------------------+------------+--------------------------------------------------------------+----------------------------+--------------------+-----------------------+------------------------+------------------------+-------------+
| reader | product_api | {bcrypt}removed | read | client_credentials,password,refersh_token,authorization_code | http://localhost:8080/home | ROLE_PRODUCT_USER | 10800 | 2592000 | NULL | NULL |
| writer | product_api | {bcrypt}removed | read,write | client_credentials,password,refersh_token,authorization_code | http://localhost:8080/home | ROLE_PRODUCT_ADMIN | 10800 | 2592000 | NULL | NULL |
+-----------+--------------+----------------------------------------------------------------------+------------+--------------------------------------------------------------+----------------------------+--------------------+-----------------------+------------------------+------------------------+-------------+
分析
- API 无需授权即可正常工作
- 如果我们只通过权威机构进行授权(@PreAuthorize("hasAuthority('...')")),它就可以正常工作
- Authientication.OAuth2Request 到达时缺少范围(空列表), OAuth2ExpressionUtils --> hasAnyScope()。
- 范围由授权服务器的 /user 端点提供
{authorities=[{id=4, authority=ROLE_PRODUCT_USER}], 详细信息={remoteAddress=127.0.0.1, sessionId=null, tokenValue=2f54e499-e47a-45fe-a6f6-e4c9593f9841,tokenType=Bearer, 解码细节=空},认证=真, userAuthentication={authorities=[{id=4, authority=ROLE_PRODUCT_USER}], 详细信息={clinet_id=读者,grant_type=密码, 用户名=product_user},认证=true,主体={密码=null, 用户名=product_user,权限=[{id=4, authority=ROLE_PRODUCT_USER}],accountNonExpired=true, accountNonLocked=true, credentialsNonExpired=true, enabled=true}, 凭据=null,名称=product_user},凭据=, oauth2Request={clientId=reader, scope=[read], requestParameters={clinet_id=reader, grant_type=password, 用户名=product_user},resourceIds=[product_api], 当局=[{authority=ROLE_PRODUCT_USER}],批准=真, refresh=false,redirectUri=null,responseTypes=[],extensions={}, grantType=密码,refreshTokenRequest=null}, 主体={密码=null,用户名=product_user,权限=[{id=4, authority=ROLE_PRODUCT_USER}],accountNonExpired=true, accountNonLocked=true, credentialsNonExpired=true, enabled=true}, clientOnly=false, name=product_user}
-
但在 UserInfoTokenServices.extractAuthentication() 中创建 OAuth2Request 时不会持久化
private OAuth2Authentication extractAuthentication(Map<String, Object> map) { Object principal = getPrincipal(map); List<GrantedAuthority> authorities = this.authoritiesExtractor .extractAuthorities(map); OAuth2Request request = new OAuth2Request(null, this.clientId, null, true, null, null, null, null, null); UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( principal, "N/A", authorities); token.setDetails(map); return new OAuth2Authentication(request, token); }
这里的第5个参数是代表作用域的字符串Set,作为null传递!
OAuth2Request 请求 = 新 OAuth2Request(null, this.clientId, null, 真,空, 空,空,空,空);
我在这里缺少任何配置吗?
【问题讨论】:
标签: java spring spring-boot spring-security oauth-2.0