java REST 应用程序中的基本身份验证
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15947415/
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
Basic authentication in REST-application
提问by Sami
Environment:
环境:
- JAVA
- Glassfish
- REST-services in different machine
- HTML5-client with AJAX and JQuery
- Jersey
- 爪哇
- 玻璃鱼
- 不同机器上的 REST 服务
- 带有 AJAX 和 JQuery 的 HTML5 客户端
- 球衣
This is what I have implemented so far:
这是我迄今为止实施的:
HTML5-client ###
HTML5 客户端###
$('#btnSignIn').click(function () {
var username = $("#username").val();
var password = $("#password").val();
function make_base_auth(user, password) {
var tok = user + ':' + password;
var final = "Basic " + $.base64.encode(tok);
console.log("FINAL---->" + final);
alert("FINAL---->" + final);
return final;
}
$.ajax({
type: "GET",
contentType: "application/json",
url: "http://localhost:8080/SesameService/webresources/users/secured/login",
crossDomain: true,
dataType: "text",
async: false,
data: {},
beforeSend: function (xhr) {
xhr.setRequestHeader('authorization', make_base_auth(username, password));
},
success: function () {
alert('Thanks for your signin in! ');
},
error: function (jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
alert(' Error in signIn-process!! ' + textStatus);
}
});
});
SERVER
服务器
In Security, I haven't got Security Manager enabled, it is disabled!
在安全中,我没有启用安全管理器,它被禁用了!
I have configured BASIC-authentication to Glassfish and my web.xml looks like that:
我已经为 Glassfish 配置了 BASIC 身份验证,我的 web.xml 看起来像这样:
<servlet-mapping>
<servlet-name>ServletAdaptor</servlet-name>
<url-pattern>/webresources/*</url-pattern>
</servlet-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>REST Protected resources</web-resource-name>
<description/>
<url-pattern>/users/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
<role-name>customer</role-name>
<role-name>user</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>jdbcRealm</realm-name>
</login-config>
<security-role>
<role-name>admin</role-name>
</security-role>
<security-role>
<role-name>user</role-name>
</security-role>
<security-role>
<description/>
<role-name>customer</role-name>
</security-role>
GLASSFISH
青鱼
LOG
日志
FINE: [Web-Security] Setting Policy Context ID: old = null ctxID = SesameService/SesameService
FINE: [Web-Security] hasUserDataPermission perm: ("javax.security.jacc.WebUserDataPermission" "/webresources/users/secured/login" "GET")
FINE: [Web-Security] hasUserDataPermission isGranted: true
FINE: [Web-Security] Policy Context ID was: SesameService/SesameService
FINE: [Web-Security] hasResource isGranted: true
FINE: [Web-Security] hasResource perm: ("javax.security.jacc.WebResourcePermission" "/webresources/users/secured/login" "GET")
QUESTION:
问题:
If I encrypt (NOT encode) password in client when user is signing up and transfer it under the SSL/HTTPS, is this secure and good way to implement this?
If I use REST-service without client, it is always open, WHY? No BASIC-authentication? Have I understood something wrong with those url-patterns?
http://localhost:8080/SesameService/webresources/users/secured/login
IF I get this working how to test that, because now if I authenticate once, I am authorised always? Is it possible to "log out" programatically inside the REST-service or in generally how to implement Log out?
When using Authorization in header with mandatory base64-encoded username:password do I have to encode my username and password to DB as well? I tried that and added Encoding (allowed values are Hex and Base64) to jdbcRealm to Glassfish and it seems that password is enough, but what happens when both are encoded in client?
如果我在用户注册时在客户端加密(不编码)密码并在 SSL/HTTPS 下传输它,这是实现这一点的安全和好方法吗?
如果我在没有客户端的情况下使用 REST 服务,它总是打开的,为什么?没有 BASIC 认证?我理解那些 url-pattern 有什么问题吗?
http://localhost:8080/SesameService/webresources/users/secured/login
如果我得到这个工作如何测试,因为现在如果我验证一次,我总是被授权?是否可以在 REST 服务中以编程方式“注销”或通常如何实现注销?
当在带有强制 base64 编码的用户名:密码的标头中使用授权时,我是否也必须将我的用户名和密码编码为 DB?我试过了,并将编码(允许值为 Hex 和 Base64)添加到 jdbcRealm 到 Glassfish,似乎密码就足够了,但是当两者都在客户端编码时会发生什么?
UPDATE:I changed web.xml and now BASIC-authentication is working when calling REST-service straight in browser :http://localhost:8080/SesameService/users/secured/login
更新:我更改了 web.xml,现在在浏览器中直接调用 REST-service 时 BASIC-authentication 正在工作:http://localhost:8080/SesameService/users/secured/login
Changes:
变化:
- I enabled security manager in Glassfish
I changed url-pattern
ServletAdaptor /*----> I took webresources off. It was generated by Netbeans
I changed the url to service to this:
http://localhost:8080/SesameService/users/secured/login
- 我在 Glassfish 中启用了安全管理器
我改变了网址模式
ServletAdaptor /*----> 我关闭了 webresources。它是由 Netbeans 生成的
我将要服务的网址更改为:
http://localhost:8080/SesameService/users/secured/login
Now I get a HTTP/1.1 401 Unauthorized when trying to authenticate from HTML5-client.
现在我在尝试从 HTML5 客户端进行身份验证时得到 HTTP/1.1 401 Unauthorized。
Request headers:`
请求标头:`
Origin: http://localhost:8383
Host:`localhost:8080`
Connection:keep-alive
Access-Control-Request-Method:GET
Access-Control-Request-Headers:authorization,content-type`
Response:
回复:
x-powered-by:Servlet/3.0 JSP/2.2 (GlassFish Server Open Source Edition 3.1.2.2 Java/Oracle Corporation/1.7)
WWW-Authenticate:Basic realm="jdbcRealm"
Server:GlassFish Server Open Source Edition 3.1.2.2
Pragma:No-cache
Expires:Thu, 01 Jan 1970 02:00:00 EET
Date:Sat, 13 Apr 2013 15:25:06 GMT
Content-Type:text/html
Content-Length:1073
Cache-Control:no-cache
UPDATE 2
更新 2
When I try to authenticate with JavaScript + Authorization-header I got 401 error and that in the log:
当我尝试使用 JavaScript + Authorization-header 进行身份验证时,我收到 401 错误,并在日志中显示:
FINE: [Web-Security] Setting Policy Context ID: old = null ctxID = SesameService/SesameService
FINE: [Web-Security] hasUserDataPermission perm: ("javax.security.jacc.WebUserDataPermission" "/users/secured/login" "OPTIONS")
FINE: [Web-Security] hasUserDataPermission isGranted: true---->!!!!!!!!!!!!!
FINE: [Web-Security] Policy Context ID was: SesameService/SesameService
FINE: [Web-Security] Codesource with Web URL: file:/SesameService/SesameService
FINE: [Web-Security] Checking Web Permission with Principals : null------->!!!!!!!
FINE: [Web-Security] Web Permission = ("javax.security.jacc.WebResourcePermission" "/users/secured/login" "OPTIONS")
FINEST: JACC Policy Provider: PolicyWrapper.implies, context (SesameService/SesameService)- result was(false) permission (("javax.security.jacc.WebResourcePermission" "/users/secured/login" "OPTIONS"))
FINE: [Web-Security] hasResource isGranted: false------->!!!!!!!!!
FINE: [Web-Security] hasResource perm: ("javax.security.jacc.WebResourcePermission" "/users/secured/login" "OPTIONS")
FINEST: JACC Policy Provider: PolicyWrapper.getPermissions(cs), context (null) codesource ((null <no signer certificates>)) permissions: java.security.Permissions@5d4de3b0 (
****UPDATE 3**** I can't be the first and only person who is trying to authenticate using BASIC in cross domain case. I changed my cross origin filters like that: response.getHttpHeaders().putSingle("Access-Control-Allow-Headers", "Authorization");
****更新 3**** 我不能成为第一个也是唯一一个在跨域情况下尝试使用 BASIC 进行身份验证的人。我改变了我的跨源过滤器: response.getHttpHeaders().putSingle("Access-Control-Allow-Headers", "Authorization");
NO 401 error anymore but still error in JavaScript. IN Glassfish log:
不再出现 401 错误,但 JavaScript 中仍然存在错误。在 Glassfish 日志中:
FINEST: JACC Policy Provider:
getPolicy (SesameService/SesameService) is NOT in service----->!!!!!!!!
FINE: JACC Policy Provider: file arrival check type: granted arrived: false exists: false lastModified: 0 storedTime: 1365968416000 state: deleted SesameService/SesameService
FINE: JACC Policy Provider: file arrival check type: excluded arrived: false exists: false lastModified: 0 storedTime: 0 state: deleted SesameService/SesameService
FINE: TM: getTransaction: tx=null, tm=null
FINE: TM: componentDestroyedorg.apache.catalina.servlets.DefaultServlet@227fe9a8
FINE: TM: resourceTable before: 0
FINE: TM: resourceTable after: 0
BTW, because I have never get this work, does this work same way than calling the REST-service direct in its own domain. So, First client requests, server requests and username-password window opens, then client request and server authenticate and response the page? I am trying to get it: Request with Authorization header in it, response from server with result from the rest service and that's it. Any idea how to secure REST-services? Easier than that? This is impossible.
顺便说一句,因为我从来没有得到过这项工作,所以这项工作的工作方式与直接在其自己的域中调用 REST 服务的方式相同。那么,首先客户端请求,服务器请求和用户名密码窗口打开,然后客户端请求和服务器验证并响应页面?我正在尝试获取它:其中包含授权标头的请求,来自服务器的响应以及来自其余服务的结果,仅此而已。知道如何保护 REST 服务吗?比那容易吗?这是不可能的。
UPDATE 4
更新 4
I just tried to move my HTML5-client to under java web-project, just pure html-pages and under same domain and BASIC-authentication is working 100 %. So the reason is because of cross-domain environment.
我只是试图将我的 HTML5 客户端移动到 java web 项目下,只是纯 html 页面和相同的域下,并且 BASIC 身份验证工作 100%。所以原因是因为跨域环境。
回答by Andrew Alcock
If I encrypt (NOT encode) password in client when user is signing up and transfer it under the SSL/HTTPS, is this secure and good way to implement this?
如果我在用户注册时在客户端加密(不编码)密码并在 SSL/HTTPS 下传输它,这是实现这一点的安全和好方法吗?
Most probably you should simply pass the password in plain text over SSL/HTTPS.
很可能您应该简单地通过 SSL/HTTPS 以纯文本形式传递密码。
All communication in SSL/HTTPS is encrypted, so it's probably not a good idea to encrypt the password as well unless you need to ensure the web server (technically the HTTPS terminator) cannot see the password.
SSL/HTTPS 中的所有通信都是加密的,因此加密密码可能不是一个好主意,除非您需要确保 Web 服务器(从技术上讲是 HTTPS 终结器)无法看到密码。
If I use REST-service without client, it is always open, WHY? No BASIC-authentication? Have I understood something wrong with those url-patterns?
如果我在没有客户端的情况下使用 REST 服务,它总是打开的,为什么?没有 BASIC 认证?我理解那些 url-pattern 有什么问题吗?
Not sure I understand the question. However BASIC auth is not a good pattern for authentication in REST because it is passing the password in plain text.
不确定我理解这个问题。然而,BASIC auth 不是 REST 中的身份验证的好模式,因为它以纯文本形式传递密码。
IF I get this working how to test that, because now if I authenticate once, I am authorised always? Is it possible to "log out" programatically inside the REST-service or in generally how to implement Log out?
如果我得到这个工作如何测试,因为现在如果我验证一次,我总是被授权?是否可以在 REST 服务中以编程方式“注销”或通常如何实现注销?
In Basic Auth, the username and password are passed by the client in every HTTP request. If the credentials are not passed by the client, then the server rejects the request. As such there is no concept of session.
在 Basic Auth 中,用户名和密码由客户端在每个 HTTP 请求中传递。如果客户端未传递凭据,则服务器拒绝该请求。因此没有会话的概念。
However, as far as the Java EE server is concerned, the login creates a user session and future requests by the same user will use the same session. If you so configure it, this session will time out.
但是,就 Java EE 服务器而言,登录会创建一个用户会话,并且同一用户以后的请求将使用相同的会话。如果您这样配置它,此会话将超时。
If logging out is important (i.e. control user sessions), then you have to create a servlet (/logout) for this which invalidates the HTTP session.
如果注销很重要(即控制用户会话),那么您必须为此创建一个 servlet (/logout),这会使 HTTP 会话无效。
The standard Java security model works as follows: When the user logs in to a security realm, the Java EE server stores a secure cookie in your browser. The browser sends this cookie back to the Java EE server in each request to the same realm. The Java EE server checks for this cookie in every request and uses it to identify the user, connecting the request to the user's session.
标准 Java 安全模型的工作原理如下:当用户登录到安全领域时,Java EE 服务器会在您的浏览器中存储一个安全 cookie。浏览器在对同一领域的每个请求中将此 cookie 发送回 Java EE 服务器。Java EE 服务器在每个请求中检查这个 cookie 并使用它来识别用户,将请求连接到用户的会话。
So you probably want to do have the REST service in the same security realm as the web application, so the browser and server work seamlessly.
因此,您可能希望在与 Web 应用程序相同的安全领域中使用 REST 服务,以便浏览器和服务器无缝工作。
When using Authorization in header with mandatory base64-encoded username:password do I have to encode my username and password to DB as well? I tried that and added Encoding (allowed values are Hex and Base64) to jdbcRealm to Glassfish and it seems that password is enough, but what happens when both are encoded in client?
当在带有强制 base64 编码的用户名:密码的标头中使用授权时,我是否也必须将我的用户名和密码编码为 DB?我试过了,并将编码(允许值为 Hex 和 Base64)添加到 jdbcRealm 到 Glassfish,似乎密码就足够了,但是当两者都在客户端编码时会发生什么?
No, don't encode the username or password - this is all handled deep within the browser and Glassfish. If you encode in the client, the client will encode the encoding and the server will reject the password.
不,不要对用户名或密码进行编码 - 这一切都在浏览器和 Glassfish 内部处理。如果你在客户端编码,客户端会编码,服务器会拒绝密码。
Could you tell me if is it possible to use j_security_check from html5-page with javaScript or am I in problems again :) I have made couple of Primefaces + jsf-application where I used FORM-auth and there wasn't any problem, but this has been totally disaster case for me.
你能告诉我是否可以在 html5-page 中使用 j_security_check 和 javaScript 还是我又遇到了问题:) 我已经制作了几个 Primefaces + jsf 应用程序,在那里我使用了 FORM-auth 并且没有任何问题,但是这对我来说完全是灾难性的案例。
You should be able to get this working comfortably with j_security_check assuming that the RESTful services stay in the same domain and security realm (then logging on to the web application will allow the browser to send the correct cookie to the REST URIs).
假设 RESTful 服务保持在相同的域和安全领域(然后登录到 Web 应用程序将允许浏览器将正确的 cookie 发送到 REST URI),您应该能够使用 j_security_check 轻松地完成这项工作。
Do note, however, that other applications will have difficulty accessing the REST services. Basically, they will have to log in via the j_security_check and then maintain the cookies sent by Glassfish. If you do need other applications to access these services programmatically, then you will need another solution:
但是请注意,其他应用程序将难以访问 REST 服务。基本上,他们必须通过 j_security_check 登录,然后维护 Glassfish 发送的 cookie。如果您确实需要其他应用程序以编程方式访问这些服务,那么您将需要另一种解决方案:
- You can set up the security realm to allow different authenticators to be 'sufficient'; set up HTTP BASIC Auth as welland make sure that none are marked 'necessary'
- Deploy the RESTful services twice, the other being a different URI. If you want to use HTTP BASIC Auth, this might be a SSL/HTTPS end point to ensure the passwords are handled securely
- 您可以设置安全领域以允许不同的身份验证器“足够”;设置HTTP基本认证以及与确保没有被标记为“必要”
- 部署 RESTful 服务两次,另一个是不同的 URI。如果您想使用 HTTP BASIC Auth,这可能是一个 SSL/HTTPS 端点,以确保安全地处理密码
回答by aditya Goyal
I am working on Spring MVC framework with spring security and use basic Authentication:
我正在使用 Spring Security 开发 Spring MVC 框架并使用基本身份验证:
Basically, in HTTP basic authentication the username and password are converted into a key or access token with the help of Base64 class (from util package).
And that key is set into the header of the HTTP URL and then hit to the server.
It works fine and at server: decode the username password key by same Base64 and then match with the database username and password. if authentication done then further process.
This give the safety of API Urls. Every body not access easily.
基本上,在 HTTP 基本身份验证中,用户名和密码在 Base64 类(来自 util 包)的帮助下转换为密钥或访问令牌。
并且该键被设置到 HTTP URL 的标头中,然后命中服务器。
它在服务器上工作正常:通过相同的 Base64 解码用户名密码密钥,然后与数据库用户名和密码匹配。如果身份验证完成,则进一步处理。
这提供了 API Urls 的安全性。每个身体都不容易进入。
Code: Create access token (key) code is:
代码:创建访问令牌(密钥)代码是:
NOTE: we pass string in Base64(from util package) "YourUsername:YourPassword" for encoding.
Yes, username and password are ":" separated.
注意:我们在 Base64(来自 util 包)“YourUsername:YourPassword”中传递字符串进行编码。
是的,用户名和密码是用“:”分隔的。
String encodeddata = new String(Base64.encodeBase64("username:password".getBytes()));
System.out.println("encodedBytes " + encodeddata);
Code for setting key into header:
将密钥设置为标题的代码:
//REQUEST TO URL WITH KEY
//请求带密钥的URL
HttpClient client = HttpClientBuilder.create().build();
HttpGet get = new HttpGet("http://google.com/Abc/api/vou/redeemed");
get.setHeader("Authorization", "Basic "+encodeddata); //key getting above
Note: You can use this with both Get and Post method. Here I am using only with get method. //RESPONSE
注意:您可以将它与 Get 和 Post 方法一起使用。这里我只使用 get 方法。//回复
HttpResponse jresponse = client.execute(get);
System.out.println("Response Code : "+ jresponse.getStatusLine().getStatusCode());
BufferedReader rd = new BufferedReader(new InputStreamReader(jresponse.getEntity().getContent()));
StringBuffer result = new StringBuffer();
String line = "";
while ((line = rd.readLine()) != null) {
result.append(line);
}
System.out.println("------Response is ::: "+ result);
//DECODE Key
//解码键
String usernameAndPassword = null;
try {
usernameAndPassword = new String(Base64.getDecoder().decode(encodedUserPassword));
} catch (Exception e) {
System.out.println("SERVER_ERROR");
}
System.out.println(usernameAndPassword);
final StringTokenizer tokenizer = new StringTokenizer(encodedUserPassword, ":");
final String username = tokenizer.nextToken();
final String password = tokenizer.nextToken();
System.out.println(username);
System.out.println(password);