Java 如何在 Dropwizard 中对资源进行基本身份验证
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20662871/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to do Basic Authentication of a resource in Dropwizard
提问by birdy
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
假设用户使用/myapp/signin
端点从浏览器或本机移动应用程序前端登录。那么我如何保护另一个端点,例如,/myapp/{username}/getstuff
它需要用户登录
@GET
@Path("/myapp/{username}/getstuff")
public Stuff getStuff(@PathParam("username") String username) {
//some logic here
return new Stuff();
}
采纳答案by shahshi15
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).
当您尝试实施 REST 时,有两件事。一个是身份验证(似乎你已经让它工作了),另一个是授权(这就是我相信你的问题)。
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:
我之前在 dropwizard 中处理它的方式是,在每个用户登录时,您将某种 access_token(这证明他们经过身份验证)返回给客户端,他们必须在他们作为某些的一部分进行的每个连续调用中返回标头(通常这是通过“授权”标头完成的)。在服务器端,您必须先将此 access_token 保存/映射到该用户,然后再将其返回给客户端,并且当使用该 access_token 进行所有连续调用时,您将查找与该 access_token 映射的用户并确定该用户是否是否有权访问该资源。现在举个例子:
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. If the client does not provided the "Authorization" header with the access_token you gave them, you should return 401 Unauthorized code right away.
3) 客户端返回到 /myapp/{username}/getstuff。如果客户端没有提供带有 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. If it does not, return 401 unauthorized code, and if it does have access, return the actual data back.
4) 如果客户端确实提供了 access_token,您可以根据您在步骤 2 中保存的 access_token 查找用户,并检查该 userId 是否有权访问该资源。如果没有,则返回 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? No it doesn't. This is where the Security Filters help in dropwizard. Here's an example to how to add security filter.
现在来到“授权”标题部分。您可以使用“@Context HttpServletRequest hsr”参数在所有调用中访问“Authoroziation”标头,但在每次调用中添加该参数是否有意义?不,它没有。这是安全过滤器在 dropwizard 中的帮助之处。以下是如何添加安全过滤器的示例。
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:
请记住,您的网址 /myapp/signin 和 /myapp/{username}/getstuff 都将通过此安全过滤器,但是 /myapp/signin 不会有 access_token,显然是因为您没有提供任何给客户呢。这必须在过滤器本身中处理,例如:
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:
您保护的 url 将取决于您的 url 的结构以及您想要保护的内容。您设计的网址越好,编写安全过滤器来保护它们就越容易。添加此安全过滤器后,流程将如下所示:
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
1) 用户转到 /myapp/signin。该调用将通过过滤器,并且由于该“if”语句,它将继续访问 /myapp/signin 的实际资源,并且您将根据成功的身份验证分配一个 access_token
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.
2) 用户使用 access_token 调用 /myapp/{username}/mystuff。此调用将通过相同的安全过滤器,并将通过您实际进行授权的“else”语句。如果授权通过,调用将继续到您实际的资源处理程序,如果未授权,则应返回 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!
我希望这有帮助!我花了大约 2 天的时间来自己解决这个问题!
回答by Harihara Vinayakaram
Sorry for being a simple user . I believe you can protect the resource by using a @Auth User user
对不起,我是一个简单的用户。我相信您可以通过使用 @Auth User 用户来保护资源
public Service1Bean Service1Method1(
@Auth User user,
@QueryParam("name") com.google.common.base.Optional<String> name) {