我的API中的主要数据信息和信息链接到项目(实体), 什么是密码流的好方法:使用spring security和OAuth2管理链接到项目的特定权限?
在此应用程序中,您有 5微服务:
缩放权限:
每个用户可以拥有多个项目,并且可以拥有每个项目的权限:
我有很多想法,但我不确定我是否有好的方法:
使用案例:我想要保护端点:
http://catalog-service/{project_key}/catalogs
只有拥有项目{project_key}权限VIEW_CATALOG或MANAGE_CATALOG的用户才能列出项目中的所有目录
我的第一个想法:使用预授权使用ProjectAccessExpression
CatalogController.java
@Controller
public class CatalogController {
@PreAuthorize("@projectAccessExpression.hasPermission(#projectKey, 'manageCatalog', principal)" +
" or @projectAccessExpression.hasPermission(#projectKey, 'viewCatalog', principal)")
@RequestMapping(
value = "/{projectKey}/catalogs",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE
)
public @ResponseBody List<Catalog> findByProject(@PathVariable("projectKey") String projectKey) {
return catalogService.find();
}
}
ProjectAccessExpression.java
@Component
public class ProjectAccessExpression {
private RestTemplate restTemplate;
public boolean havePermission(String projectKey, String permission , String username) {
Boolean havePermission = restTemplate.getForObject(String.format("http://uaa-service/permission/check?project=%1&permission=%2&username=%3",
projectKey, permission, username
), Boolean.class);
return havePermission;
}
}
不方便:每次都需要拨打UAA服务
第二个想法:使用USER_ROLE
使用user_role
SHOP1 SHOP2 是projectKey
不方便:我不确定,但如果用户更改权限,我需要撤销所有令牌关联
第三个想法:在身份验证blob中添加特定权限
我不知道如何存储...
在控制器中注释:
@PreAuthorize("@ProjectAccessExpression.hasPermission(authentication, 'manageCatalog||viewCatalog', #projectKey)
不方便:第二种想法同样不方便
答案 0 :(得分:0)
它基本上看起来像是在尝试利用OAuth 2.0的角色为您的项目。以下是关于OAuth 2.0的一些Spring文档的摘录
将用户角色映射到范围: http://projects.spring.io/spring-security-oauth/docs/oauth2.html
有时限制令牌的范围不仅取决于分配给客户端的作用域,还取决于用户自己的权限。如果在 AuthorizationEndpoint 中使用 DefaultOAuth2RequestFactory ,则可以设置标志 checkUserScopes = true ,以将允许的范围限制为仅匹配用户角色的范围。您还可以将 OAuth2RequestFactory 注入 TokenEndpoint ,但只有在您安装 TokenEndpointAuthenticationFilter 时才能使用(即使用密码授予) - 您只需要在HTTP BasicAuthenticationFilter 之后添加该过滤器。当然,您也可以实现自己的规则,将范围映射到角色,并安装您自己的 OAuth2RequestFactory 版本。 AuthorizationServerEndpointsConfigurer 允许您注入自定义 OAuth2RequestFactory ,以便在使用 @EnableAuthorizationServer 时可以使用该功能设置工厂。
所有这些基本上归结为通过将范围映射到您自己的自定义角色,您可以保护具有不同范围的端点。这样您就可以获得非常好的安全性。
我找到了一个非常好的演练,您可以将其用作参考:(显然您必须根据自己的用例配置设置)
https://raymondhlee.wordpress.com/2014/12/21/implementing-oauth2-with-spring-security/
答案 1 :(得分:0)
我使用此解决方案并且工作正常
** 1 - 当用户签名**
时加载业务逻辑安全性此示例查找具有角色的用户在数据库中保留,并添加所有角色依赖项目。操作后我有身份验证令牌 GrantedAuthority:ROLE_USER,ROLE_MANAGE_CATALOG:project1,ROLE_VIEW_PROFILE:project1,ROLE_MANAGE_PROJECT:project2,...
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<User> user = userService.findByLogin(username);
if (!user.isPresent()) {
Object args[] = {username};
throw new UsernameNotFoundException(
messageSource.getMessage("user.notexist", args, "User {0} doesn't not exist", LocaleContextHolder.getLocale())
);
}
if (!user.get().isActivated()) {
//throw new UserNotActivatedException(String.format("User %s was not activated!", username));
Object args[] = {username};
throw new UserNotActivatedException(
messageSource.getMessage("user.notactivated", args, "User {0} was not activated", LocaleContextHolder.getLocale()));
}
//Here implement your proper logic
//Add busness logic security Roles
// eg ROLE_MANAGE_PROJECT:{project_key}, ROLE_MANAGE_CATALOG:{project_key}
List<Role> bRoles = projectService.getRolesForUser(username)
user.get().getRoles().addAll(
bRoles
);
UserRepositoryUserDetails userDetails = new UserRepositoryUserDetails(user.get());
return userDetails;
}
}
** 2使用预授权表达式检查安全性**
在此示例中,只有拥有此权限的用户才能执行此操作:
ROLE_MANAGE_PROJECT:{projectKey}
@PreAuthorize(“@ oauthUserAccess.hasPermission(authentication,'”+ Constants.PP_MANAGE_PROJECT +“',#projectKey)”) @RequestMapping( 值= “/项目/ {projectKey}”, method = RequestMethod.PUT, produce = MediaType.APPLICATION_JSON_VALUE ) public ResponseEntity updateProject(@PathVariable(“projectKey”)String projectKey,@ Valid @RequestBody Project project)
OauthUserAccess类:
@Component("oauthUserAccess")
public class OauthUserAccess {
/**
* Check if it is the administrator of the application IMASTER
* @param authentication
* @param projectKey
* @return
*/
public boolean hasAdminPermission(OAuth2Authentication authentication, String projectKey) {
if(authentication.getOAuth2Request().getAuthorities().contains("ROLE_ADMIN")) return true;
return false;
}
/**
*
* @param authentication
* @param permissionType
* @param projectKey
* @return
*/
public boolean hasPermission(OAuth2Authentication authentication, String permissionType, String projectKey) {
if (!ProjectPermissionType.exist(permissionType) ||
projectKey.isEmpty() ||
!projectKey.matches(Constants.PROJECT_REGEX))
return false;
if (authentication.isClientOnly()) {
//TODO check scope permission
if(authentication.getOAuth2Request().getScope().contains(permissionType+":"+projectKey)) return true;
}
if (hasAdminPermission(authentication, projectKey)) return true;
String projectPermission = "ROLE_" + permissionType + ":" + projectKey;
String projectPermissionManage = "ROLE_" + permissionType.replace("VIEW", "MANAGE") + ":" + projectKey;
String manageProject = "ROLE_" + Constants.PP_MANAGE_PROJECT + ":" + projectKey;
Predicate<GrantedAuthority> p = r -> r.getAuthority().equals(projectPermission) || r.getAuthority().equals(projectPermissionManage) || r.getAuthority().equals(manageProject);
if (authentication.getAuthorities().stream().anyMatch(p)) {
return true;
};
return false;
}
}
3 - 优势/劣势
<强>优势强>
业务逻辑权限仅在用户登录应用程序时加载,而不是每次都加载,因此它是微服务架构的强大解决方案。
<强>缺点强>
当权限更改时,需要更新身份验证令牌或撤消令牌 当您更新用户权限时,用户需要注销并登录。但是,如果没有此安全逻辑,则会遇到相同的问题,例如,当用户被禁用或启用时。
我在解决方案中使用的解决方案:
newAuthorities = projectService.getRolesForUser(username);
UsernamePasswordAuthenticationToken newAuth = new UsernamePasswordAuthenticationToken(auth.getPrincipal(), auth.getCredentials(), newAuthorities);
OAuth2Authentication authentication = (OAuth2Authentication)SecurityContextHolder.getContext().getAuthentication();
Collection<OAuth2AccessToken> accessTokens = tokenStore.findTokensByUserName(principal.getName());
OAuth2Authentication auth2 = new OAuth2Authentication(authentication.getOAuth2Request(), newAuth);
accessTokens.forEach(token -> {
if (!token.isExpired()) {
tokenStore.storeAccessToken(token, auth2);
}
});