简体   繁体   English

如何在Dropwizard中进行资源的基本身份验证

[英]How to do Basic Authentication of a resource in Dropwizard

I believe I have basic authentication working but I'm not sure how to protect resources so that they can only be accessed when the user is signed in. 我相信我有基本的身份验证工作,但我不知道如何保护资源,以便只有在用户登录时才能访问它们。

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();
    }
}

My Signin resource is like this: 我的Signin资源是这样的:

@Path("/myapp")
@Produces(MediaType.APPLICATION_JSON)
public class UserResource {
    @GET
    @Path("/signin")
    public User signin(@Auth User user) {
        return user;
    }
}

And I sign the user with: 我签署了用户:

~/java/myservice $ curl -u "someuser" http://localhost:8080/myapp/signin
Enter host password for user 'someuser':
{"name":"someuser"}

Question

Let's say the user signs in from a browser or native mobile app front end using the /myapp/signin endpoint. 假设用户使用/myapp/signin端点从浏览器或本机移动应用程序前端/myapp/signin How then can I protect another endpoint, say, /myapp/{username}/getstuff which requires a user to be signedin 那我怎么能保护另一个端点,比如/myapp/{username}/getstuff ,这需要用户签名

@GET
@Path("/myapp/{username}/getstuff")
public Stuff getStuff(@PathParam("username") String username) {
    //some logic here
    return new Stuff();
}

There are 2 things when you are trying to implement REST. 当您尝试实现REST时,有两件事情。 One is Authentication (which seems that you have got it working) and other is Authorization (which is what I believe your question is). 一个是身份验证(似乎你已经使它工作)而另一个是授权(这是我相信你的问题)。

The way I have handled it in dropwizard before is, with every user signin, you return some kind of access_token (this proves they authenticated) back to the client which has to be returned by them in EVERY successive call they make as a part of some header (normally this is done through "Authorization" header). 之前我在dropwizard中处理它的方式是,在每个用户登录的情况下,你将某种access_token(这证明它们经过身份验证)返回给客户端,客户端必须在每次连续调用中返回它们作为某些部分的一部分。标题(通常这是通过“授权”标题完成的)。 On the server side, you will have to save/map this access_token to THAT user before returning it back to the client and when all the successive calls are made with that access_token, you look up the user mapped with that access_token and determine if that user is authorized to access that resource or not. 在服务器端,您必须将此access_token保存/映射到该用户,然后再将其返回给客户端,并且当使用该access_token进行所有后续调用时,您将查找使用该access_token映射的用户并确定该用户是否有权访问该资源。 Now an example: 现在举个例子:

1) User signs in with /myapp/signin 1)用户使用/ myapp / signin登录

2) You authenticate the user and send back an access_token as a response while saving the same on your side, such as, access_token --> userIdABCD 2)您对用户进行身份验证并发送一个access_token作为响应,同时保存在您身边,例如access_token - > userIdABCD

3) The client comes back to /myapp/{username}/getstuff. 3)客户端返回/ myapp / {username} / getstuff。 If the client does not provided the "Authorization" header with the access_token you gave them, you should return 401 Unauthorized code right away. 如果客户端未提供带有access_token的“Authorization”标头,则应立即返回401 Unauthorized代码。

4) If the client does provide the access_token, you can look up the user based on that access_token you saved in step # 2 and check if that userId has access to that resource of not. 4)如果客户端确实提供了access_token,您可以根据您在步骤#2中保存的access_token查找用户,并检查该userId是否可以访问该资源。 If it does not, return 401 unauthorized code, and if it does have access, return the actual data back. 如果没有,则返回401未授权代码,如果有访问权限,则返回实际数据。

Now coming ot the "Authorization" header part. 现在来到“授权”标题部分。 You could get access to "Authoroziation" header in all of your calls using the "@Context HttpServletRequest hsr" parameter but does it make sense to add that parameter in each call? 您可以使用“@Context HttpServletRequest hsr”参数访问所有调用中的“Authoroziation”标头,但是在每次调用中添加该参数是否有意义? No it doesn't. 不,不。 This is where the Security Filters help in dropwizard. 这是安全过滤器在dropwizard中的帮助。 Here's an example to how to add security filter. 这是一个如何添加安全过滤器的示例。

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 ...
}

Now, which resource does this security filter really protects? 现在,这个安全过滤器真正保护了哪些资源? For that you will need to add this filter to the specific resources you want to protect which can be done as follows: 为此,您需要将此过滤器添加到要保护的特定资源,可以按如下方式完成:

environment.addFilter(SecurityFilter, "/myapp/*");

Remember on thing here that both your urls /myapp/signin and /myapp/{username}/getstuff, both will go through this security filter, BUT, /myapp/signin will NOT have an access_token, obviously because you haven't given any to the client yet. 请记住,你的urls / myapp / signin和/ myapp / {username} / getstuff都将通过这个安全过滤器,但是/ myapp / signin将没有access_token,显然是因为你没有给出任何到了客户端。 That wil have to be taken care of in the filter itself such as: 必须在过滤器本身中处理,例如:

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
}

The url that you are protecting will depend on the how your urls are structured and what you want to protect. 您保护的URL将取决于您的网址结构以及您想要保护的内容。 The better urls you design, the easier it will be to write security filters for their protection With the addition of this security filter the flow will be like this: 您设计的网址越好,编写安全过滤器以保护它们就越容易。添加此安全过滤器后,流程将如下所示:

1) User goes to /myapp/signin. 1)用户转到/ myapp / signin。 The call will go through the filter and because of that "if" statement, it will continue to your ACTUAL resource of /myapp/signin and you will assign an access_token based on successful authentication 该调用将通过过滤器,并且由于该“if”语句,它将继续到/ myapp / signin的ACTUAL资源,您将根据成功的身份验证分配access_token

2) User makes a call to /myapp/{username}/mystuff with the access_token. 2)用户使用access_token拨打/ myapp / {username} / mystuff。 This call will go through the same security filter and will go through the "else" statement where you actually do your authorization. 此调用将通过相同的安全筛选器,并将通过您实际授权的“else”语句。 If the authorization goes through, the call will continue to you actual resource handler, and if not authorized, 401 should be returned. 如果授权通过,则调用将继续到您的实际资源处理程序,如果未授权,则应返回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");
        }
    }
}

I hope this helps! 我希望这有帮助! Took me about 2 days to figure this out myself! 我花了大约两天的时间来弄明白这一点!

Sorry for being a simple user . 很抱歉是一个简单的用户。 I believe you can protect the resource by using a @Auth User user 我相信您可以使用@Auth用户来保护资源

public Service1Bean Service1Method1(
    @Auth User user,
    @QueryParam("name") com.google.common.base.Optional<String> name) {

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM