如何做一个Dropwizard资源基本身份验证 [英] How to do Basic Authentication of a resource in Dropwizard

查看:1210
本文介绍了如何做一个Dropwizard资源基本身份验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我相信我有基本身份验证工作,但我不知道怎么当用户签署保护资源,使它们只能被访问。

 公共类SimpleAuthenticator实现的Authenticator< BasicCredentials,使用者名称> {
    UserDAO的userDAO的;    公共SimpleAuthenticator(UserDAO的userDAO的){this.userDao = userDAO的;}    @覆盖
    公众可选<使用者>验证(BasicCredentials凭证)抛出的AuthenticationException
    {
        用户的用户= this.userDao.getUserByName(credentials.getUsername());
        如果(用户= NULL和放大器;!&安培;
                。user.getName()equalsIgnoreCase(credentials.getUsername())及&放大器;
                BCrypt.checkpw(credentials.getPassword(),user.getPwhash())){
            返回Optional.of(新用户(credentials.getUsername()));
        }
        返回Optional.absent();
    }
}

我的签到资源是这样的:

  @Path(/ MyApp的)
@Produces(MediaType.APPLICATION_JSON)
公共类UserResource {
    @得到
    @Path(/登入)
    公共用户登入(@Auth用户用户){
        返回用户;
    }
}

和我签订的用户:

 〜/ JAVA / $的MyService卷曲-uSomeUser的HTTP://本地主机:8080 / MyApp的/登入
输入用户SomeUser的主机密码:
{名:指someUser}

比方说,使用 / MyApp的/登入端点从浏览器或本地移动应用前端用户的迹象。那么如何我可以保护另一个端点,比方说, / MyApp的/ {用户名} / getstuff 其中要求用户必须signedin

  @GET
@Path(/ MyApp的/ {用户名} / getstuff)
公物getStuff(@PathParam(用户名)字符串username){
    //此处一些逻辑
    返回新的东西();
}


解决方案

有,当你试图实现REST两件事情。一个是认证(这似乎你已经得到了它的工作)等是授权(这是什么,我相信你的问题是)。

我在dropwizard处理它们之前,与每一个用户登入的方式,返回某种的access_token(这证明他们身份验证),回到它必须由他们在每连续调用返回的客户,他们做出一个一些头(通常这是通过授权头完成)的一部分。在服务器端,你将不得不保存/返回回给客户端之前与的access_token映射用户映射此的access_token给该用户,当所有的后续调用与该的access_token做,你看,而且如果用户决定被授权访问该资源或没有。现在一个例子:

1)用户登录与/ MyApp的/登入

2)您验证用户身份,并发送回一个作为的access_token同时节约在你身边一样,比如,一个的access_token响应 - > userIdABCD

3)客户端回来/ MyApp的/ {用户名} / getstuff。如果客户端不具备你给他们的access_token授权头,你应该返回401未授权code的时候了。

4)如果客户端提供的access_token,你可以查看基于用户的access_token您在步骤#2中保存,并检查是否该用户标识已获得的不是资源。如果没有,返回401未经授权code和如果它确实有访问,返回的实际数据后面。

现在来OT授权头的一部分。你可以得到使用@Context HttpServletRequest的HSR参数获得Authoroziation头在所有的通话,但它是有意义的添加参数每次调用?不,它不需要。这是安全过滤器在dropwizard帮助。下面是如何添加安全过滤器的例子。

 公共类SecurityFilter类扩展OncePerRequestFilter {
@覆盖
保护无效doFilterInternal(HttpServletRequest的请求,HttpServletResponse的响应,FilterChain filterChain)抛出了ServletException,IOException异常{
字符串的accessToken = request.getHeader(授权);
//在这里做基于访问令牌的东西(检查用户的授权到资源...
}

现在,它的资源此安全过滤器真的可以保护?对于您将需要此过滤器添加到要保护哪些可以做具体的资源如下:

  environment.addFilter(SecurityFilter类,/ MyApp的/ *);

记住的东西在这里,无论您的网址/的myapp /登入和/ MyApp的/ {用户名} / getstuff,双方将通过这个保安过滤器,但是,/ MyApp的/登入不会有一个的access_token,显然是因为你的天堂式给出任何给客户呢。这预示了在过滤器来照顾自己,如:

 字符串的URL = request.getRequestURL()的toString()。
如果(url.endsWith(登入))
{
//不要找授权头,并让滤波器通没有任何检查
}
其他
{
//做你的常规授权相关的东西在这里
}

这是要保护将取决于如何你的URL的结构,并要保护什么的URL。更好的网址,你的设计,就越容易编写安全过滤器保护他们通过添加这种安全过滤器的流量将是这样的:

1)用户进入/ MyApp的/登入。呼叫将转到通过过滤器,因为如果的声明,它将继续/ MyApp的/登入你的实际资源,你将根据成功验证分配的access_token

2)用户打电话到/ MyApp的/ {用户名} /的MyStuff用的access_token。这个调用将通过相同的安全过滤器将通过其他人语句,你确实做你的授权。如果授权通过,通话将继续为您实际的资源处理程序,如果没有授权,401应退还。

 公共类SecurityFilter类扩展OncePerRequestFilter

{

  @覆盖
保护无效doFilterInternal(HttpServletRequest的请求,HttpServletResponse的响应,FilterChain filterChain)抛出了ServletException,IOException异常
{
    。字符串URL = request.getRequestURL()的toString();
    字符串的accessToken = request.getHeader(授权);
    尝试
    {
        如果(的accessToken == NULL || accessToken.isEmpty())
        {
            抛出新的异常(Status.UNAUTHORIZED.getStatus code(),只要访问令牌null或空或没有权限访问该资源。+的accessToken);
        }
        如果(url.endsWith(/登入))
        {
//不要做任何事
            filterChain.doFilter(请求响应);
        }
        其他
        {
//此处授权的access_token。如果授权通过,继续正常,或抛出一个401 unaurhtorized例外            filterChain.doFilter(请求响应);
        }
    }
    赶上(异常前)
    {
        response.setStatus(401);
        response.setCharacterEncoding(UTF-8);
        response.setContentType(MediaType.APPLICATION_JSON);
        response.getWriter()打印(未授权)。
    }
}

}

我希望这有助于!我花了大约2天算出这个喽!

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:

@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. How then can I protect another endpoint, say, /myapp/{username}/getstuff which requires a user to be signedin

@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. 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). 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. Now an example:

1) User signs in with /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

3) The client comes back to /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.

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. If it does not, return 401 unauthorized code, and if it does have access, return the actual data back.

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? No it doesn't. This is where the Security Filters help in 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. 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. 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. 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

2) User makes a call to /myapp/{username}/mystuff with the access_token. This call will go through the same security filter and will go through the "else" statement where you actually do your authorization. If the authorization goes through, the call will continue to you actual resource handler, and if not authorized, 401 should be returned.

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!

这篇关于如何做一个Dropwizard资源基本身份验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆