preauthorize - spring security hasrole multiple
Cómo usar expresiones personalizadas en Spring Security @ PreAuthorize/@ PostAuthorize anotaciones (3)
1) Primero debe MethodSecurityExpressionRoot
a MethodSecurityExpressionRoot
que contiene una funcionalidad adicional específica del método. La implementación original de Spring Security es un paquete privado y, por lo tanto, no es posible simplemente extenderlo. Sugiero verificar el código fuente para la clase dada.
public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
// copy everything from the original Spring Security MethodSecurityExpressionRoot
// add your custom methods
public boolean isAdmin() {
// do whatever you need to do, e.g. delegate to other components
// hint: you can here directly access Authentication object
// via inherited authentication field
}
public boolean isOwner(Long id) {
// do whatever you need to do, e.g. delegate to other components
}
}
2) A continuación, debe implementar MethodSecurityExpressionHandler
personalizado que utilizará el CustomMethodSecurityExpressionRoot
definido CustomMethodSecurityExpressionRoot
.
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
@Override
public void setReturnObject(Object returnObject, EvaluationContext ctx) {
((MethodSecurityExpressionRoot) ctx.getRootObject().getValue()).setReturnObject(returnObject);
}
@Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
MethodInvocation invocation) {
final CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
root.setThis(invocation.getThis());
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(this.trustResolver);
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
}
3) Defina el bean controlador de expresiones en su contexto, por ejemplo, a través de XML puede hacerlo de la siguiente manera
<bean id="methodSecurityExpressionHandler"
class="my.package.CustomMethodSecurityExpressionHandler">
<property name="roleHierarchy" ref="roleHierarchy" />
<property name="permissionEvaluator" ref="permissionEvaluator" />
</bean>
4) Registrar el manejador definido arriba.
<security:global-method-security pre-post-annotations="enabled">
<security:expression-handler ref="methodSecurityExpressionHandler"/>
</security:global-method-security>
5) Luego solo use las expresiones definidas en sus @PreAuthorize
y / o @PostAuthorize
@PreAuthorize("isAdmin() or isOwner(#id)")
public void deleteGame(@PathVariable int id, @ModelAttribute currentGame) {
// do whatever needed
}
Y una cosa más. No es muy común utilizar la seguridad a nivel de método para proteger los métodos de los controladores, sino más bien para proteger los métodos con lógica empresarial (también conocidos como métodos de capa de servicio). Entonces podrías usar algo como el siguiente.
public interface GameService {
// rest omitted
@PreAuthorize("principal.admin or #game.owner = principal.username")
public void delete(@P("game") Game game);
}
Pero ten en cuenta que esto es solo un ejemplo. Se espera que el principal real tenga el método isAdmin()
y que el juego tenga el método getOwner()
que devuelva el nombre de usuario del propietario.
¿Hay una manera de crear declaraciones más expresivas en los bloques de @Preauthorize? Aquí hay un ejemplo de algo que me estoy repitiendo, porque @Preauthorize no es muy inteligente listo para usar.
@RequestMapping(value = "{id}", method = RequestMethod.DELETE)
public void deleteGame(@PathVariable int id, @ModelAttribute User authenticatingUser) {
Game currentGame = gameService.findById(id);
if(authenticatingUser.isAdmin() || currentGame.getOwner().equals(authenticatingUser)) {
gameService.delete(gameService.findById(id));
} else {
throw new SecurityException("Only an admin, or an owner can delete a game.");
}
}
Lo que preferiría es algo así.
@RequestMapping(value = "{id}", method = RequestMethod.DELETE)
@Preauthorize(isAdmin(authenicatingUser) OR isOwner(authenicatingUser, id)
public void deleteGame(@PathVariable int id, @ModelAttribute User authenticatingUser, @ModelAttribute currentGame ) { //I''m not sure how to add this either :(
gameService.delete(gameService.findById(id));
}
Parte del problema es que necesito hacer una consulta a la base de datos para obtener algunas de estas cosas para verificar los permisos, como consultar la base de datos para obtener una copia del juego, y luego comparar el propietario del juego con la persona que hace la solicitud. No estoy realmente seguro de cómo funciona todo eso en el contexto de un procesador de anotación @Preauthorize, o cómo agrego cosas a la colección de objetos disponibles en el atributo de valor @Preauthorize ("").
Como @PreAuthorize
evalúa las SpEl , la forma más sencilla es apuntar a un bean:
@PreAuthorize("@mySecurityService.someFunction()")
MySecurityService.someFunction
debe tener el tipo de retorno boolean
.
Spring-security proporcionará automáticamente una variable llamada authentication
si desea pasar el objeto de Authentication
. También puede usar cualquier expresión SpEl válida para acceder a los argumentos pasados a su método seguro, evaluar expresiones regulares, llamar a métodos estáticos, etc. Por ejemplo:
@PreAuthorize("@mySecurityService.someFunction(authentication, #someParam)")
Podrías escribir tu anotación algo como:
@PreAuthorize("hasRole(''ROLE_ADMIN'') and hasPermission(#id, ''Game'', ''DELETE'')")
Para que la parte hasPermission funcione, necesita implementar la interfaz PermissionEvaluator .
Luego define un bean manejador de expresiones:
@Autowired
private PermissionEvaluator permissionEvaluator;
@Bean
public DefaultMethodSecurityExpressionHandler expressionHandler()
{
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
handler.setPermissionEvaluator(permissionEvaluator);
return handler;
}
E inyecte en su configuración de seguridad:
<global-method-security pre-post-annotations="enabled">
<expression-handler ref="expressionHandler" />
</global-method-security>