java - Cómo hacer la autenticación básica de un recurso en Dropwizard
rest authentication (2)
Creo que la autenticación básica funciona, pero no estoy seguro de cómo proteger los recursos para que solo se pueda acceder a ellos cuando el usuario haya iniciado sesión.
public class SimpleAuthenticator implements Authenticator<BasicCredentials, User> {
UserDAO userDao;
public SimpleAuthenticator(UserDAO userDao) {this.userDao = userDao;}
@Override
public Optional<User> authenticate(BasicCredentials credentials) throws AuthenticationException
{
User user = this.userDao.getUserByName(credentials.getUsername());
if (user!=null &&
user.getName().equalsIgnoreCase(credentials.getUsername()) &&
BCrypt.checkpw(credentials.getPassword(), user.getPwhash())) {
return Optional.of(new User(credentials.getUsername()));
}
return Optional.absent();
}
}
Mi recurso de inicio de sesión es así:
@Path("/myapp")
@Produces(MediaType.APPLICATION_JSON)
public class UserResource {
@GET
@Path("/signin")
public User signin(@Auth User user) {
return user;
}
}
Y firmo al usuario con:
~/java/myservice $ curl -u "someuser" http://localhost:8080/myapp/signin
Enter host password for user ''someuser'':
{"name":"someuser"}
Pregunta
Supongamos que el usuario /myapp/signin
desde un navegador o una aplicación móvil nativa utilizando el punto final /myapp/signin
. Entonces, ¿cómo puedo proteger otro punto final, por ejemplo, /myapp/{username}/getstuff
que requiere que un usuario inicie sesión?
@GET
@Path("/myapp/{username}/getstuff")
public Stuff getStuff(@PathParam("username") String username) {
//some logic here
return new Stuff();
}
Hay 2 cosas cuando intentas implementar REST. Una es la autenticación (que parece que la tienes funcionando) y otra es la autorización (que es lo que creo que es tu pregunta).
La forma en que lo he manejado en Dropwizard antes es que, con cada inicio de sesión de usuario, devuelve algún tipo de access_token (esto prueba que se autenticaron) al cliente, el cual debe ser devuelto por CADA llamada sucesiva que haga como parte de algunos encabezado (normalmente esto se hace a través del encabezado "Autorización"). En el lado del servidor, tendrá que guardar / asignar este access_token a ESE usuario antes de devolverlo al cliente y cuando se realicen todas las llamadas sucesivas con ese access_token, busque el usuario asignado con ese access_token y determine si ese usuario Está autorizado para acceder a ese recurso o no. Ahora un ejemplo:
1) El usuario inicia sesión con / myapp / signin
2) Usted autentica al usuario y envía un access_token como respuesta mientras guarda el mismo en su lado, como, access_token -> userIdABCD
3) El cliente vuelve a / myapp / {username} / getstuff. Si el cliente no proporcionó el encabezado de "Autorización" con el access_token que le proporcionó, debe devolver el código 401 no autorizado de inmediato.
4) Si el cliente proporciona el access_token, puede buscar al usuario según el access_token que guardó en el paso # 2 y verificar si ese ID de usuario tiene acceso a ese recurso o no. Si no lo hace, devuelva 401 código no autorizado y, si tiene acceso, devuelva los datos reales.
Ahora viene la parte de cabecera de "Autorización". Puede obtener acceso al encabezado "Autoroziation" en todas sus llamadas usando el parámetro "@Context HttpServletRequest hsr" pero ¿tiene sentido agregar ese parámetro en cada llamada? No, no lo hace. Aquí es donde los Filtros de seguridad ayudan en dropwizard. Aquí hay un ejemplo de cómo agregar un filtro de seguridad.
public class SecurityFilter extends OncePerRequestFilter{
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException{
String accessToken = request.getHeader("Authorization");
// Do stuff here based on the access token (check for user''s authorization to the resource ...
}
Ahora, ¿qué recurso protege realmente este filtro de seguridad? Para ello, deberá agregar este filtro a los recursos específicos que desea proteger, lo que se puede hacer de la siguiente manera:
environment.addFilter(SecurityFilter, "/myapp/*");
Recuerde que aquí tanto su urls / myapp / signin como / myapp / {username} / getstuff, ambos pasarán por este filtro de seguridad, PERO, / myapp / signin NO tendrá un access_token, obviamente porque no ha dado ningún al cliente todavía. Eso tendrá que ser cuidado en el propio filtro como:
String url = request.getRequestURL().toString();
if(url.endsWith("signin"))
{
// Don''t look for authorization header, and let the filter pass without any checks
}
else
{
// DO YOUR NORMAL AUTHORIZATION RELATED STUFF HERE
}
La url que está protegiendo dependerá de la forma en que estén estructuradas y de lo que quiera proteger. Mientras mejores urls diseñes, más fácil será escribir filtros de seguridad para su protección. Con la adición de este filtro de seguridad, el flujo será así:
1) El usuario va a / myapp / signin. La llamada pasará por el filtro y debido a esa declaración "if", continuará hasta su recurso ACTUAL de / myapp / signin y le asignará un access_token basado en la autenticación exitosa
2) El usuario realiza una llamada a / myapp / {username} / mystuff con el símbolo de acceso. Esta llamada pasará por el mismo filtro de seguridad y pasará por la declaración "else" donde usted hace su autorización. Si se aprueba la autorización, la llamada continuará hasta su gestor de recursos real y, si no está autorizado, se devolverá el 401.
public class SecurityFilter extends OncePerRequestFilter
{
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException
{
String url = request.getRequestURL().toString();
String accessToken = request.getHeader("Authorization");
try
{
if (accessToken == null || accessToken.isEmpty())
{
throw new Exception(Status.UNAUTHORIZED.getStatusCode(), "Provided access token is either null or empty or does not have permissions to access this resource." + accessToken);
}
if (url.endsWith("/signin"))
{
//Don''t Do anything
filterChain.doFilter(request, response);
}
else
{
//AUTHORIZE the access_token here. If authorization goes through, continue as normal, OR throw a 401 unaurhtorized exception
filterChain.doFilter(request, response);
}
}
catch (Exception ex)
{
response.setStatus(401);
response.setCharacterEncoding("UTF-8");
response.setContentType(MediaType.APPLICATION_JSON);
response.getWriter().print("Unauthorized");
}
}
}
¡Espero que esto ayude! Me tomó cerca de 2 días para resolver esto por mí mismo!
Lo siento por ser un usuario simple. Creo que puedes proteger el recurso usando un usuario de @Auth User
public Service1Bean Service1Method1(
@Auth User user,
@QueryParam("name") com.google.common.base.Optional<String> name) {