精华内容
下载资源
问答
  • 手机app端,token的详解

    千次阅读 2021-01-07 16:04:39
    token是什么? token是服务端生成的一串字符串,以作客户端进行请求的一个令牌。当第一次登陆后,服务器生成一个token便将此token返回个客户端,以后客户端只需带上这个token前来请求数据即可,无需再次带上用户名...

    https://blog.csdn.net/dddxxxx/article/details/88685155

     

    token是什么?

     token是服务端生成的一串字符串,以作客户端进行请求的一个令牌。当第一次登陆后,服务器生成一个token便将此token返回个客户端,以后客户端只需带上这个token前来请求数据即可,无需再次带上用户名和密码。


    如何使用token?

    两种使用方式:

    1、用设备号/设备mac地址作为token

    客户端:客户端在登录的时候获取设备的设备号/mac地址,并将其作为参数传递到服务端。


    服务端:服务端收到该参数后,使用一个变量来接收同时将其作为token保存到数据库中,并将该token设置到session中,客户端每次请求的时候都要统一拦截,并将客户端传递的token和服务端session中的token进行对比,如果形同则放行,不同则拒绝。


    分析:此刻客户端和服务器端就统一了一个唯一的标识Token,而且保证了每一个设备拥有了一个唯一的会话。该方法的缺点是客户端需要带设备号/mac地址作为参数传递,而且服务器端还需要保存;优点是客户端不需重新登录,只要登录一次以后一直可以使用,至于超时的问题是有服务器这边来处理,如何处理?若服务器的Token超时后,服务器只需将客户端传递的Token向数据库中查询,同时并赋值给变量Token,如此,Token的超时又重新计时。

    2、用session值作为token:

     客户端:客户端只需要携带用户名和密码登录即可。

     服务端:客户端接收到用户名和密码后并判断,如果正确了就将本地获取sessionID作为Token返回给客户端,客户端以后只需带上请求数据即可。

     

     

    展开全文
  • Web App Token验证的5个要点

    千次阅读 2016-09-26 22:05:58
    原文地址:...5 KEYS TO WEB APP TOKEN AUTHENTICATION Web App Token验证的5个要点 There are many scenarios where using token-based au

    原文地址:http://www.jamiekurtz.com/2014/11/25/5-keys-to-web-app-token-authentication/

    5 KEYS TO WEB APP TOKEN AUTHENTICATION

    Web App Token验证的5个要点

    There are many scenarios where using token-based authentication is desired, but leveraging OAuth-based authentication against Facebook or Twitter in your web application or RESTful API isn’t possible.
    很多情况下使用基于taken的验证服务是有必要的,但是在你的网站应用中利用对Facebook或Twitter的OAuth的验证却不是必须的。

    As a consultant, where the bulk(大量) of the sites and web APIs we build are line-of-business applications over existing databases (specifically, existing users), most of the time we can’t simply forgo our own custom authentication. In fact, I don’t believe I’ve ever seen an enterprise application where taking a user’s Twitter-provided OAuth token would have been an acceptable solution. We simply aren’t working on social-enabled web applications. Nor are we building mobile apps where customers want their users to log in with social accounts.
    作为参考,我们创建的大量的网站和web APIs都是在现有数据库(特别是现存的用户)之上的业务应用层,大部分时候我们不能简单地放弃我们自己的自定义验证。事实上,我相信我还没有见过一个企业应用使用用的Twitter OAuth认证作为可接受的解决方案。我们没有实现社交功能的网页应用。我们也不会创建让用户想要通过他们社交账号登录的手机应用。

    But that doesn’t mean we need to completely throw out token-based authentication. In fact, I much prefer leveraging JSON Web Tokens for traditional server-rendered web sites, Single Page Applications making AJAX calls, and RESTful APIs supporting mobile applications. In the end, I believe claims-based tokens are an excellent way to know who’s accessing your application and what they are allowed to do – even for the cases where you need to utilize existing usernames and passwords in an existing database or other credential store.
    但是这并不意味着我们需要完全抛弃基于token的验证机制。事实上,我更喜欢在传统服务器渲染的网页,单页应用使用AJAX请求和提供移动应用的RESTful APIs上使用JSON Web Tokens。最后,我相信基于声明的tokens是一个不错的方式去知道是谁访问了你的应用,并且他们能够做些什么,甚至是其它你需要使用到数据库或其它凭据存储中用户名和密码的地方。

    In this post I want to share five keys to understanding token-based authentication in your web and mobile applications. This is not an explanation of how OAuth works, nor is it instructions for implementing one of the OAuth grant types. In fact, I’m not going to show you any code! Crazy, I know. But as I spend time helping developers architect and implement authentication/authorization, the main gap seems to be an understanding of the basic idea of token-based authentication. There doesn’t seem to be much trouble finding an appropriate library or blocks of code on Stack Overflow.
    在这篇文章中,我想分享在你的网页和移动应用中使用基于Token验证的5个要点。这里不会解释OAuth认证是如何工作的,也不会说明如何实现一个OAuth授权类型的。其实,我不会向你展示任何代码!我知道这听上去很不可思议。但是在我花费了大量时间帮助开发人员构建和实现了认证或授权时,最主要的鸿沟还是对于基于token认证机制基本概念的理解。

    As such, the purpose of this post is to help you understand the underlying flow of data involved in utilizing tokens in those cases where you need to validate user credentials against an existing set of usernames and passwords in some internal database. I.e. those cases where OAuth with Facebook simply isn’t an option.
    这篇文章的目的是帮助你了解当你需要使用token在某个内部数据库中通过用户名和密码来验证用户凭证时相关数据的潜在的流程。

    FIVE KEYS

    5个要点

    Let’s look at what I consider to be five keys to understanding token-based authentication. First, in this context, a token is just a collection of claims. A claim is simply a key-value pair. For example, username: bsmith, or email: bsmith@example.com. To see an example of and create your own JSON Web Token (JWT), you can visit my Online JSON Web Token Builder. On that page you will see that the format of a JWT is very simple – again, just a collection of key-value pairs that “claim” something about the caller: “My name is Bob Smith, and my email address is bsmith@example.com”, etc. Some claims are special (e.g. expiration date). My online token builder includes a handful of these special claims. You can see them all here.
    让我们看看我所认为的了解基于token认证机制的5个要点。首先,在这篇文中,token只是一个声明的集合。一个声明只是一个简单的键值对。比如,username:bsmith或者email: bsmith@example.com。想要看示例或者创建你自己的JSON Web Token,你可以访问我的Online JSON Web Token Builder。在那里你将看到一个JWT的格式是如此简单,当然也是一个键值对的集合,这个声明就类似于“My name is Bob Smith, and my email address is bsmith@example.com”等等。有些声明十分特别(比如,到期日期)。我的在线token生成器包含了少部分这样特别的声明。你可以在那里看到它们。

    Here’s an example token (in raw form, prior to signing and base64 encoding):
    这里有一个token示例:

    {
            "iss": "Online JWT Builder",
            "iat": 1416797419,
            "exp": 1448333419,
            "aud": "www.example.com",
            "sub": "jrocket@example.com",
            "GivenName": "Johnny",
            "Surname": "Rocket",
            "Email": "jrocket@example.com",
            "Role": [
                "Manager",
                "Project Administrator"
            ]
    }

    The first five claims in that token are a subset of the special – or, reserved – claims. Most libraries used in processing JSON Web Tokens will understand these special claims.
    在这个token中前五个声明是特别或者保留声明的子集。大部分用于处理JSON Web Tokens的库都会理解这些特别的声明。

    Now that we know a JWT is just a collection of claims, the second key is to make sure these claims include enough user properties sufficient to avoid hitting the database on subsequent requests. This is important for a couple of reasons. First, and most obvious, you can avoid the performance hit of performing the exact same database call on every single web or AJAX request. The idea here is that the user’s properties (e.g. email address, first and last name, roles) will likely not change from one request to another. So we don’t really need to take the database hit on every request.
    现在我们知道了一个JWT只是一个声明的集合,第二个要点是保证这些声明包含了足够的用户信息能够避免在接下来的请求中去访问数据库。这点十分重要是有多个原因的。首先,且最明显的,你可以避免在每个网页或AJAX请求重复执行相同数据库请求时带来的性能问题。这儿的思路是用户的属性(比如,email address, first and last name, roles)在重复的请求中可能不会改变。所以我们没有必要每个请求都去访问数据库。

    Second, you will find that a stand-alone token – one that contains all relevent claims for the user – is much easier to work with in development, testing, and production troubleshooting. And since our token includes all that we need to know the identity and roles of the caller, we can simply convert the claims to a User (e.g. an IUserPrincipal in .NET, a simple User object literal in NodeJS). This operation is a mere copying of claims from the token to properties on a user object. Remember, a database call to get more information is not required.
    第二,你会发现一个独立的token——包含了用户所有相关声明——在开发,测试和生产排除故障时更加方便使用。当我们的token包含了所有我们需要了解的关于请求的身份和角色信息时,我们可以很容易地将声明转换成一个用户(比如,.NET中的一个 IUserPrincipal,NodeJS中一个简单的User对象)。这个操作仅仅是从token中复制声明到用户对象的属性中。请记住,一个想要获得更多信息的数据库请求是不被允许的。

    At this point we have a token that contains claims that tell us all we need to know about a user/caller. Exactly how you validate a user’s credentials and generate the token will depend on your platform and selected library. But in the end, you authenticate the user, generate a token, and associate it with the caller. For a web site, this is typically done with a browser cookie. For a non-browser client, we simply provide the token for them to submit in the header of subsequent HTTP requests. The third key, then, is that your web site will examine the incoming cookie collection and the HTTP Authorization header for the expected token. We don’t really care where it is, as long as the token is present on all HTTP requests.
    基于这一点,我们已经有了一个token,它包含我们对于一个用户或请求所需要知道的全部信息。你如何验证用户的凭据或生成token取决于你的平台和所选的库。最后,你认证用户,生成token,将它和请求关键到一起。对于一个网站而言,这通常都是靠浏览器cookie来完成的。对于非浏览器的客户端,我们会在它们提交的的HTTP请求头文件中提供token信息。然后,你的网站会检查这些进来的cookie集合和HTTP授权头文件中的token。我们不关心它在哪,正如token会在所有的HTTP请求中一样。

    And with that, we arrive at the fourth key. Because we are checking both the cookie collection and the HTTP Authorization header, we can support both browser and non-browser clients all with the same token and same token validation code. Any HTTP GET or POST, for example, a page request or a form submission, will include the cookie that contains the token that identifies the user. The same applies to AJAX requests from JavaScript/jQuery code – i.e. AJAX requests automatically include all cookies for the web site. And finally, any non-browser clients calling into your site – e.g. a mobile application consuming your REST API – will submit the token via the HTTP AUthorization header. Same token, same token validation code, just a different spot in the HTTP request.
    然后我们到了第四部。因为我们同时检查了cookie集合和HTTP授权头文件,我们可以给浏览器或非浏览器提供相同的token和验证码。比如任何HTTP的GET或POST请求,一个页面请求或表单提交都会在cookie中包含拥有用户认证信息的token。这也应用到了JavaScript/jQuery代码中的AJAX请求中。比如,AJAX请求会自动包含所有的网站cookies。最后,你网站中任何非浏览器客户端请求,比如一个手机应用的REST API请求,会通过HTTP授权的头文件来提交token信息。相同的token,相同的token验证码,只是在HTTP请求的不同位置。

    Now, you maybe wondering… if any caller can just toss a token on an HTTP request to our web site, how do we know it is valid? Or, more accurately, how do we know that we generated the token? In essence, how do we know the token’s content (i.e. its claims) can be trusted? In the world of web site security, trust is paramount. We absolutely must be able to trust that the claims in the token are true and haven’t been tampered with. For this, we sign the token. If a token is signed, then we can trust it was created by our own site. This is our fifth and final key. Validation of the token’s signature is typically left to a 3rd party library of some kind, so we won’t go into those details here.
    现在,你可能会想,如果一个请求只是在向我们网站中的HTTP请求中扔了一个token信息,我们怎么知道它是否合法呢?或者更准确的说,我们怎么知道是我们生成的这个token信息?本质上就是我们如何知道token的内容是可以被信任的?在网站安全领域,信任至上。我们绝对必须能够相信token中的声明是可以被信任的,没有被篡改过的。所以,我们会对token签名。如果token被签名了,我们就可以相信它是由我们的网站生成的。这是我们的第五也是最后的要点。验证token的签名回流到后面的第三部分来说明,所以我们在这里不会细说。

    To summarize the five keys… we are using what’s called a JSON Web Token (JWT), which is simply a collection of claims the caller is making about himself. Because the token includes all claims necessary to both identify and authorize the caller, we can avoid querying the database on every request in order to fetch user properties. We will examine the HTTP request’s cookie collection and Authorization header for the token, which allows both browser-based and non browser-based clients to securely use our web site and API. And finally, because the token was signed by our own site, and we can easily verify its signature on every HTTP request, we can take the token’s claims as truth, and simply copy those individual claims to a user object associated with the request. In this way all downstream code will have access to the caller’s name, email address, roles, favorite color, date of birth, whatever is needed.
    总结一下这五个要点,我们使用被称为JSON Web Token (JWT)的简单声明集合来让请求表明自己的身份。因为token包含了对于这个请求所有必要识别和授权声明,我们可以避免每次为了查询用户信息而重复请求数据库。我们会检查HTTP请求cookie和头文件中token信息,使得浏览器或非浏览器客户端都能安全的使用我们网站和API。最后,因为token被我们的网站签名了,所以我们可以很容易验证每个HTTP请求的严明,我们可以相信token声明中的信息,并且在请求中复制这些单个声明到用户对象中。正因如此,所有接下来的代码都可以访问用户的姓名,电子邮件地址,角色,最喜欢的颜色,生日等任何需要的信息。

    LOGGING IN

    登录

    The first thing a user is going to do on your site is log in. As such, we need to make sure the login process not only validates the user’s credentials, but also creates our signed JWT with all necessary claims. Then the application just needs to set the newly created JWT as a cookie on the HTTP response. This process is shown below. Note that I haven’t referenced any specific language or library. This is intentional as the process is the same no matter your underlying platform.
    用户在你的网站做的第一件事就是登录。所以,我们不仅需要确认登录过程中的用户凭证,也需要创建包含了所有必要声明信息的签名JWT。然后,应用只需要把最新创建的JWT放入HTTP请求的cookie中。流程如下。需要注意的是我这里没有指明任何语言和库。这是一个通用的流程,不论你使用的何种平台。

    To start the login process, the user’s browser posts his/her credentials to your server code – i.e. some kind of login route or action. The route or action will validate those credentials against the existing credential store. Assuming the credentials are valid, the server code will then get the user information from the database (or, other user store) – for example, a Users table in a database, or a user record in Active Directory.
    在登录开始时,用户的浏览器提交他/她的凭证到你的服务器。比如某种登录路由或操作。它会通过现存的凭证库来验证这些凭证。假设这些凭证合法,服务器会从数据库(或其他用户存储)中获得用户的信息。比如,一个数据库中的用户表或者一条活动目录中的用户记录。

    Using the various properties on the new user object, your code will create claims for a token, accumulating the claims into token data. Remember, the claims are just name-value pairs. And those claims should include enough information for you to simply convert them into a full-blown user object on subsequent requests (which we’ll look at shortly). If working within a NodeJS application, these claims would look similar to the raw JSON text shown earlier in this post. Don’t forget to set the “special” reserved claims – e.g. iss, exp, aud, sub.
    使用新用户对象上的各种信息,你的代码会生成一个token声明,并将其加入到token数据中。请记住,声明仅仅是键值对。并且,这些声明包含了足够的信息让你在接下来的请求中将其转换成一个完整的用户对象。如果是使用的NodeJS应用,那么这些声明看上去会和上文中提交的JSON文本类似。不要忘了设置那些特别的保留声明,比如 iss, exp, aud, sub。

    Next, in order to create a signed JWT, we need a signing key. In most cases, this key can be just about anything. Some libraries are pickier than others, so make sure you use a key that is appropriate for your given platform and JWT library. My own Online JSON Web Token Builder web site allow you to create 32, 64, and 128 byte keys. Some libraries will also support the use of asymmetric private keys – e.g. from an X.509 certificate. The JwtAuthForWebAPI .NET Nuget package I created allows for either symmetric keys or certificates. Regardless, you would typically use the platform’s config system to retrieve a signing key.
    下一步,为了创建一个签名的JWT,我们需要一个签名密钥。在大多数情况下,这个密钥可以是任何东西。一些库可能比其他的更加挑剔,所以确定你使用的密钥适用于你的平台和JWT库。我自己的 Online JSON Web Token Builder网站允许你创建32,64和128位的密钥。一些库也支持使用不对称的私钥,比如X.509凭证。我创建的JwtAuthForWebAPI .NET Nuget包可以使用对成加密证书。无论如何,你通常会使用平台的配置系统来得到一个签名密钥。

    Then we use some kind of library to create the actual signed and base64-encoded JWT – given the signing key we just retrieved from config. Finally, we use the current request’s response object to set a cookie whose value is our signed JWT. I usually name the cookie “usertoken” or “ut” or something similar. Remember to set the HttpOnly and Secure flags on the cookie!
    接着我们使用某些库配置中得到的签名密钥来创建实际的签名和base64编码的JWT。最后,我们使用当前请求的响应对象来设置我们签名JWT的cookie。我通常将这个cookie命名为“usertoken”或者“ut”等类似的名称。记住要在cookie中设置HttpOnly和安全标志。

    At the conclusion of this login process, the user’s browser contains a cookie that will grant them access to your web site – both web page browsing and jQuery AJAX calls (since all AJAX calls automatically include the site’s cookies). And the cookie’s value – i.e. the JWT – is signed, so you know you can trust it. If anyone tampers with the token’s claims, signature validation will fail, and you can prevent the user from further access.
    对于登录过程的总结,用户的浏览器包含了一个被授予的cookie来有权访问你的网站,包括了页面请求和JQuery AJAX请求(自从所有的AJAX请求会自动包含所有的网站cookie后)。cookie的值是被签名过得,所以你知道你可以相信它。如果有任何人修改了token的声明,签名就会失效,并且你可以防止用户进一步地访问。

    NON-BROWSER SUPPORT

    非浏览器支持

    At this point, in order to support non-browser clients, you simply need to provide a RESTful endpoint that accepts a caller’s credentials, and returns the same signed JWT we just created above. Only this time, we aren’t using the request’s response object to set a cookie. We merely want to return the token to the caller as part of a JSON response (or similar). The non-browser client will then set an HTTP Authorization header on all subsequent requests, with the header’s value being of the form: “Bearer the_jwt” – sans quotes.
    就这一点而言,为了支持非浏览器客户端,你只需要提供一个RESTful的节点来接受请求的凭证,并返回一个我们创建的签名JWT即可。只有这个时候,我们不会使用请求的响应对象来设置cookie。我们仅仅需要将token信息作为JSON响应的一部分返回给请求即可。非浏览器客户端会在所有接下来的请求中设置包含了JWT值得HTTP授权头文件。

    RESOURCE PROTECTION

    资源保护

    Now let’s examine the process of protecting your site’s resources using the signed JWT from above. Per one of our five keys defined previously, we can expect the token to exist in either the request’s cookie collection, or, in the HTTP Authorization header. Either way, we don’t care – as long as one of those slots contains the token.
    现在让我们检查使用上述签名JWT来保护你网站资源的过程。通过上述提到的5个要点,我们知道token信息存在于每个请求的cookie或HTTP头文件中。无论何种方式,我们都不关心,只要其中一个包含了token信息即可。

    The entire validation and protection process is shown below.
    整个验证和保护过程如下所示。

    The start, the client (e.g. browser, mobile application) makes an HTTP request to our server, trying to access a protected resource. By that we just mean a web page or other resource for which the caller must be authorized to view.
    一开始,客户端(比如浏览器,手机应用)会向我们的服务器发出一个HTTP请求,试图访问受保护的资源。因此我们有必要让网页和其他的资源必须在被认证后才可以查看。

    THE MIDDLEWARE HOOK

    中间件HOOK

    Per the platform we’re working with, we will have some sort of middleware hook in place to intercept the request and do some token-based validation. In ASP.NET Web API this would be a DelegatingHandler. If writing a NodeJS/ExpressJS application, a simple function callback will do.
    每一个我们创建的平台,我们都会有一些中间件hook用来拦截请求并做一些基于token的验证。在ASP.NET Web API中,它是DelegatingHandler。如果是一个NodeJS/ExpressJS应用,一个简单的函数回调便足以。

    As mentioned previously, we need to check first in the request’s cookie collection for our JWT. If not found, then we look for our token in the Authorization header. If neither location contains the token we expect, then we’re done and we let the processing of the request continue. Note that we don’t want to generate a 401 HTTP response at this point – because we don’t actually know that the requested resource even requires an authorized user.
    正如之前所提到的,我们需要先检查请求cookie中的JWT。如果没有找到,那么我们再在头文件中查找。如果其中之一的地方我们找到了需要的token,那么我们就可以让请求过程继续进行。需要注意的是我们不希望在这里生成一个401的HTTP响应,因为我们不知道所需要请求的资源是什么。

    Conversely, if the token is present, then we need to do some validation. First we retrieve our signing key from the config system. Then we use it to decode the signed and based64-encoded token string. If the signature is valid, we will end up with a token object of some kind that includes the original claims data we created during the login process (described in the previous section).
    相反,如果token存在,我们需要进行一些验证。首先我们从配置系统中检索我们的签名密钥。如果签名是有效的,我们会结束包含了我们在登录过程中创建的原始声明的token对象。

    Next we need to validate some of the claims – namely, the expiration date (exp), intended audience (aud), not valid before value (nbf), and any other claims information we feel necessary to validate. If any of the validation fails, we simple halt the validation process and let the request process continue. Again, we aren’t interested in preventing a caller from accessing a resource just yet. We’re only trying to create a valid User object.
    下一步我们需要验证某些声明,即过期时间,目标用户,无效前值等任何我们感觉有必要验证的信息。如果任意一个验证失败了,我们停止验证过程,并让请求继续。同样的,在这里我们不关心去保护请求访问某些资源。我们只是要创建一个有效的用户对象。

    Assuming validation succeeds, we then convert the token’s claims to a User object, and then set the new User object on the request thread or request object (depending on the underlying platform). Now all downstream code will have access to a User object – including any checks of the caller’s roles.
    假设验证成功了,我们将token声明转换成用户对象,然后设置新的用户对象到请求线程或对象中(取决于使用的平台)。现在下面所有的代码都能访问包含了用户角色检验信息的用户对象。

    All of the steps we just covered typically happen within the site’s middleware hook, which is intended to intercept every single HTTP request. So we haven’t yet gotten into any application routes or resources or API endpoints.
    所有上述我们提到的步骤通常都发生在网站的中用于连接所有单独HTTP请求的中间件hook中。所以我们不用深入到任何应用路由,资源或API节点中。

    RESOURCE AUTHORIZATION

    资源授权

    Once the process above completes, the actual resource or route code can execute. And since we should have a valid User object at this point, we can check that its roles allow it to access the resource. For example, in an ASP.NET MVC or Web API application this is usually accomplished with an attribute on a route or controller action. Regardless, if the user’s roles don’t allow access; or, if a user object doesn’t even exist (i.e. if the middleware code wasn’t able to successfully validate the incoming token), then we can at this point return a 401 HTTP response to the caller. Or, sometimes a 403.
    一旦过程完成,实际的资源或者代码就可以执行了。因为在这里我们需要有一个有效的用户对象,我们可以检查它的角色是否可以访问这些资源。比如,在ASP.NET MVC或Web API应用中,他们通常都是由一个路由或控制器操作完成的。无论如何,如果用户角色不允许访问,或者用户对象根本不存在(比如,中间件代码没有能够成功的验证输入的token),那么我们可以在这时向请求返回401或者403HTTP响应.

    It is important to realize that the process of looking for and converting a token into a User object is separate from that of authorizing the user for the requested resource. In some cases, for example resources that are allowed to be accessed by an anonmyous user, not finding a valid token is ok – it’s up to the resource to decide.
    需要意识到查询和转换token为用户对象和验证用户请求资源权限是分开的过程。在一些时候,比如资源可以被匿名用户访问时,不用查询有效token也是可以的,这由资源决定。

    IN CLOSING

    关闭

    I want to close by saying again that it is generally preferable to use a 3rd party token issuer or sign-on server of some kind. Leveraging a user’s Facebook or Twitter account to obtain an OAuth token is definitely a great way to go. But as I stated above, I haven’t seen too many enterprise applications where the client wanted its users to sign in with a social account. And many times an old legacy application has already been running for quite some time, and so they already have users and users’ credentials – and they don’t want to mess with those. So this post was really about using JWT-based authentication in those scenarios where you have to work with a custom credential store (e.g. a Users table in a database).
    在结尾,我想再说一次,通常使用第三方的token或签名服务是一个更好的选择。利用用户的Facebook或Twitter账号来获得一个OAuth账号是一个不错的方式。但是正如我之前所说的,我还没有见过很多企业应用程序的客户端希望它的用户通过一个社会帐户来登录。很多时候,那些旧的应用已经运行了相当长的一段时间,所以他们已经有了足够的用户和用户凭证,他们不想将其混淆。所以这篇文章真是说明如何在这种你需要使用自定义凭证库的情况下使用基于JWT的认证。

    Here again are my five keys to remember for token authentication:
    这里再强调一下记住token认证的5个要点:

    A token is just a collection of claims
    Include enough user properties sufficient to avoid hitting the database on subsequent requests
    Your web site will examine both the request’s cookie collection and HTTP Authorization header for the expected token
    Because of this, you can support both browser and non-browser clients with the same token and same token validation code
    Since the token is signed, you can trust that it was created by your own site, and so can accept the token’s claims as truth
    token只是一个声明集合
    包含了足够用户的信息,以避免在接下来的请求中访问数据库
    你的网站要同时检查请求的cookie集合和HTTP授权头文件来查找所需的token
    正因如此,你可以用相同的token和token验证码,同时支持浏览器和非浏览器客户端
    因为token是被签名的,你可以确认它是被你的网站创建的,所以它可以访问token的声明

    As promised, I didn’t show any code in this post. If you have trouble finding code or libraries that turn this information into something that works, please let me know in the comments below and I’ll put some examples together.
    正如之前提到的,我没有在这片文章中展示任何的代码。如果你对于选择代码或工具库将这些信息运用到工作中还有疑问,请在下面的留言中让我知道,我会一并举例。

    展开全文
  • 先说下背景,项目包含一个管理系统(web)和门户网站(web),还有一个手机APP(包括Android和IOS),三个系统共用一个后端,在后端使用shiro进行登录认证和权限控制。好的,那么问题来了web和APP都可以用shiro认证...

    先说下背景,项目包含一个管理系统(web)和门户网站(web),还有一个手机APP(包括Android和IOS),三个系统共用一个后端,在后端使用shiro进行登录认证和权限控制。好的,那么问题来了web和APP都可以用shiro认证吗?两者有什么区别?如果可以,解决方案是什么?看着大家焦急的小眼神,接下来挨个解决上面的问题。

    web和APP可以用shiro统一登录认证吗?
    可以。假如web和APP都使用密码登录的话,那没的说肯定是可以的,因为对于shiro(在此不会介绍shiro详细知识,只介绍本文章必要的)来说,不管是谁登录,用什么登录(用户名密码、验证码),只要通过subject.login(token)中的token告诉shiro,然后在自己定义的Realm里面给出自己的认证字段就可以了,好吧说的云里雾里,看看代码

    // 在自己登录的rest里面写,比如UserRest里面的login方法中,user为传递过来的参数
    Subject currentUser = SecurityUtils.getSubject();
    UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), user.getPassword()); 
    // 开始进入shiro的认证流程
    currentUser.login(token);
    上面的代码是开始使用shiro认证,调用subject.login(token)之后就交给shiro去认证了,接下来和我们相关的就是自定认证的Realm了,比如自定义UserRealm

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {  
            //获取基于用户名和密码的令牌  
            //实际上这个token是从UserResource面currentUser.login(token)传过来的  
            //两个token的引用都是一样的
            UsernamePasswordToken token = (UsernamePasswordToken)authcToken;  
            System.out.println("验证当前Subject时获取到token为" + ReflectionToStringBuilder.toString(token, ToStringStyle.MULTI_LINE_STYLE));  
            // 从数据库中获取还用户名对应的user
            User user = userService.getByPhoneNum(token.getUsername());  
            if(null != user){  
                AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getPhoneNum(),user.getPassword(), getName());  
                return authcInfo;  
            }else{  
                return null;  
            }
        }  

    再配一张图

    图中描述的是使用shiro进行一个完整的登录过程

    所以由以上代码看出目前我们还没有发现APP和web登录d区别,那么区别是什么呢?

    web和APP登录认证的区别
    好吧,标题不太准确,应该是登录的时候和登陆之后会话保持在web和APP之间的区别,先说登录:

    登录
    APP和PC web所需的设备不同很大程度上决定了两者之间的区别,web一般在PC上浏览,登录的时候使用用户名和密码,如果使用了记住密码就是用cookie认证,web登录有以下情况

    第一次登录,使用用户名和密码登录
    关闭浏览器、session过期,重新使用密码登录(如果有记住密码功能,可以使用cookie登录)
    用户删除cookie或者cookie过期,使用户名和密码登录
    APP在移动设备上查看,第一次登录的时候使用用户名和密码,但是以后如果不是用户主动退出,都应该保持登录状态,这样才会有更好的用户体验,但是不可能一直保留该APP的会话,也不可能把密码保存在本地,所以APP应该以下的过程

    第一次登录,使用用户名密码
    以后用户打开应用之后,用户不需输入密码系统就可以自动登录
    用户主动退出(重装等情况视为主动退出)之后,使用用户名和密码登录
    貌似没有看出什么区别,唯一的不同就是第二点:怎么不用密码登录,web使用的是cookie(由浏览器自动维护的),APP怎么登陆呢?由于APP本地不保存密码,那么也参考web,使用类似cookie的东西,我们叫他token吧,那问题就解决了,APP本地保存token,为了安全性,定期更新token,那再来看看会话的保持。

    会话(session)(保持状态)
    如果用户登录了,怎么保持登录状态呢,web有cookie和session配合解决这个问题,下面先简单说一下我对这两个东西的理解,因为APP会话就是参考这个原理设计的。

    cookie:是由浏览器维护的,每次请求浏览器都会把cookie放在header里面(如果有的话),也可以看做js的可以访问本地存储数据的位置之一(另一个就是local storage)

    session:由于http是无状态的,但是有时候服务器需要把这次请求的数据保存下来留给下一次请求使用,即需要维护连续请求的状态,这个时候服务器就借助cookie,当浏览器发送请求来服务器的时候,服务器会生成一个唯一的值,写到cookie中返回给浏览器,同时生成一个session对象,这样session和cookie值就有了一一对应关系了,浏览下一次访问的时候就会带着这个cookie值,这个时候服务器就会获得cookie的值,然后在自己的缓存里面查找是否存在和该cookie关联的session

     

    因为cookie和session的配合,shiro可以本身很好的支持web的登录和会话保持,对于APP来说也可以借鉴cookie和session的这种实现方式,唯一存在的问题,就是web的cookie是由浏览器维护的,自动将cookie放在header里面,那我们APP只要把服务器返回的cookie放在header里面,每次访问服务器的时候带上就可以了。

     

    免密码登录
    解决了登录和会话保持的问题,还剩一个免密码登陆:

    web:因为一般网页主需要记住7天密码(或者稍微更长)的功能就可以了,可以使用cookie实现,而且shiro也提供了记住密码的功能,在服务器端session不需要保存过长时间

    APP:因为APP免密码登录时间需要较长(在用户不主动退出的时候,应该一直保持登录状态),这样子在服务器端就得把session保存很长时间,给服务器内存和性能上造成较大的挑战,存在的矛盾是:APP需要较长时间的免密码登录,而服务器不能保存过长时间的session,解决办法:

    APP第一次登录,使用用户名和密码,如果登录成功,将cookie保存在APP本地(比如sharepreference),后台将cookie值保存到user表里面
    APP访问服务器,APP将cookie添加在heade里面,服务器session依然存在,可以正常访问
    APP访问服务器,APP将cookie添加在heade里面,服务器session过期,访问失败,由APP自动带着保存在本地的cookie去服务器登录,服务器可以根据cookie和用户名进行登录,这样服务器又有session,会生成新的cookie返回给APP,APP更新本地cookie,又可以正常访问
    用户手动退出APP,删除APP本次存储的cookie,下次登录使用用户名和密码登录
    这种方法存在的问题:

    cookie保存在APP本地,安全性较低,可以通过加密cookie增加安全性
    每次服务器session失效之后,得由APP再次发起登录请求(虽然用户是不知道的),但是这样本身就会增加访问次数,好在请求数量并不是很大,不过这种方式会使cookie经常更新,反而增加了安全性
    这里给出另外一种实现方式:

    实现自己的SessionDao,将session保存在数据库,这样子的好处是,session不会大量堆积在内存中,就不需要考虑session的过期时间了,对于APP这种需要长期保存session的情况来说,就可以无限期的保存session了,也就不用APP在每次session过期之后重新发送登录请求了。实现方式如下:

    为了使用Hibernate将Session保存到数据库,新建一个SimpleSessionEntity

    package org.lack.entity;
    
    import java.io.Serializable;
    
    import org.apache.shiro.session.mgt.SimpleSession;
    
    import com.phy.em.user.entity.User;
    
    public class SimpleSessionEntity {
    
        private Long id;
        private String cookie;
        private Serializable session;
        
        public Long getId() {
            return id;
        }
        public void setId(Long id) {
            this.id = id;
        }
        public Serializable entity() {
            return session;
        }
        public void setSession(Serializable session) {
            this.session = session;
        }
        public String getCookie() {
            return cookie;
        }
        public void setCookie(String cookie) {
            this.cookie = cookie;
        }
        public Serializable getSession() {
            return session;
        }
    }
    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
    
    <hibernate-mapping package="org.lack.entity">
        <class name="SimpleSessionEntity" table="session">
            <!-- 标识 -->
            <id name="id">
                <column name="id"></column>
                <generator class="increment"></generator>
            </id>
            
            <property name="session">
                <column name="session"></column>
            </property>
            
            <property name="cookie">
                <column name="cookie"></column>
            </property>
    
        </class>
    </hibernate-mapping>

    以上贴出来的是SimpleSessionEntity的映射文件,特别要注意的是Hibernate也是支持把对象保存在数据库中的,但是该实体要实现Serializable,在取出来的时候强转为对应的对象即可,所以这里session的类型为Serializable

    新建session缓存的方式的类,这里继承自EnterpriseCacheSessionDAO,可以使用ehcache作为二级缓存,一定要记得实现save、update、readSession、delete方法,特别是save方法只是保存一个基本的session,重要的attribute都是update的,在readSession中从数据库中读取即可

    package org.lack.dao
    
    import java.io.Serializable;
    import java.util.Date;
    import org.apache.log4j.Logger;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.session.UnknownSessionException;
    import org.apache.shiro.session.mgt.SimpleSession;
    import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
    import org.springframework.transaction.annotation.Transactional;
    import com.phy.em.common.dao.IBaseDao;
    import com.phy.em.common.shiro.entity.SimpleSessionEntity;
    import com.phy.em.user.entity.User;
    public class SessionEntityDao extends EnterpriseCacheSessionDAO {
        
        private IBaseDao<User> baseDao;
        private IBaseDao<SimpleSessionEntity> sessionDao;
        private Logger log = Logger.getLogger(SessionEntityDao.class);
        
        @Override
        public Serializable create(Session session) {
            // 先保存到缓存中
            Serializable cookie = super.create(session);
            // 新建一个SimpleSessionEntity,然后保存到数据库
            SimpleSessionEntity entity = new SimpleSessionEntity();
            entity.setSession((SimpleSession)session);
            entity.setCookie(cookie.toString());
            sessionDao.save(entity);
            
            return cookie;
        }
        
        @Override
        public void update(Session session) throws UnknownSessionException {
            super.update(session);
            SimpleSessionEntity entity = getEntity(session.getId());
            if(entity != null){
                entity.setSession((SimpleSession)session);    
                sessionDao.update(entity);
            }        
        }
        
        @Override
        public Session readSession(Serializable sessionId) throws UnknownSessionException {
            Session session = null;
            
            try{
                session = super.readSession(sessionId);
            } catch(Exception e){
                
            }
            
            // 如果session已经被删除,则从数据库中查询session
            if(session == null){
                SimpleSessionEntity entity = getEntity(sessionId);
                if(entity != null){
                    session = (Session) entity.getSession();    
                } 
            }
         // 如果是APP则更新lastAccessTime
           User user = getUser(sessionId);
            if(user != null){
              // 如果该用户是APP用户(user不为空说明就是),则判断session是否过期,如果过期则修改最后访问时间
              ((SimpleSession)session).setLastAccessTime(new Date());
            }
    
         return session;
        }
        
        @Override
        public void delete(Session session) {
            super.delete(session);        
        }
        
        private User getUser(Serializable sessionId){
            String hql = "from User user where user.cookie ='" + sessionId + "'";
            return baseDao.findUniqueByHQL(hql);
        }
        
        private SimpleSessionEntity getEntity(Serializable sessionId){
            String hql = "from SimpleSessionEntity entity where entity.cookie ='" + sessionId + "'";
            return sessionDao.findUniqueByHQL(hql);
        }
        
        private boolean isExpire(Session session){
            long timeout = session.getTimeout();
            long lastTime = session.getLastAccessTime().getTime();
            long current = new Date().getTime();
            if((lastTime + timeout) > current){
                return false;
            }
            return true;
        }
    
        public void setBaseDao(IBaseDao<User> baseDao) {
            this.baseDao = baseDao;
        }
    
        public void setSessionDao(IBaseDao<SimpleSessionEntity> sessionDao) {
            this.sessionDao = sessionDao;
        }
        
    }

    我快被自己蠢哭了,在继承EnterpriseCacheSessionDAO 只实现了readSession,妄想自己新建一个SimpleSession来返回给shiro使用,尝试过很多次之后不行,跟着调试了很多shiro源码,发现在SimpleSession中Shiro不仅设置了基本的属性,更重要的是设置了Attribute,但是我自己新建的SimpleSession没有,所以认证是失败的,所以在此敬告各位一定要记得实现save和update方法。

    虽然走了很多弯路,但是随着对shiro源码的调试学习,对shiro了解更深了,不再仅仅停留在只会使用的地步上,有深入。
    原文:https://blog.csdn.net/qq_30725371/article/details/80723612 

    展开全文
  • APP开发 Token生成 验证

    2016-12-19 20:59:00
    3、根据 user_id,user_token 获取表记录,如果表记录不存在,直接返回错误,如果记录存在,则进行下一步; 4、更新 user_token 的过期时间(延期,保证其有效期内连续操作不掉线); 5、返回接口数据;   ...

    准备好协议(HTTP)、数据表示方法(JSON)、请求数据的方法(REST)

    选择一个合适的框架

    接口特点汇总:

    1、因为是非开放性的,所以所有的接口都是封闭的,只对公司内部的产品有效;

    2、因为是非开放性的,所以OAuth那套协议是行不通的,因为没有中间用户的授权过程;

    3、有点接口需要用户登录才能访问;

    4、有点接口不需要用户登录就可访问;

     

    针对以上特点,移动端与服务端的通信就需要2把钥匙,即2个token。

    第一个token是针对接口的(api_token);

    第二个token是针对用户的(user_token);

     

    先说第一个token(api_token)

     

    它的职责是保持接口访问的隐蔽性和有效性,保证接口只能给自家人用,怎么做到?参考思路如下:

    现在的接口基本是mvc模式,URL基本是restful风格,URL大体格式如下:

    http://blog.snsgou.com/模块名/控制器名/方法名?参数名1=参数值1&参数名2=参数值2&参数名3=参数值3

     

    接口token生成规则参考如下:

    api_token = md5 ('模块名' + '控制器名' + '方法名' + '2013-12-18' + '加密密钥') = 770fed4ca2aabd20ae9a5dd774711de2

    其中的 

    1、 '2013-12-18' 为当天时间,

    2、'加密密钥' 为私有的加密密钥,手机端需要在服务端注册一个“接口使用者”账号后,系统会分配一个账号及密码,数据表设计参考如下:

    字段名字段类型注释
    client_idvarchar(20)客户端ID
    client_secretvarchar(20)客户端(加密)密钥

    (注:只列出了核心字段,其它的再扩展吧!!!)

     

    服务端接口校验,PHP实现流程如下:

    01<?php
    02// 1、获取 GET参数 值
    03$module $_GET['mod'];
    04$controller $_GET['ctl']
    05$action $_GET['act'];
    06$client_id $_GET['client_id'];
    07$api_token $_GET[''api_token];
    08 
    09// 2、根据客户端传过来的 client_id ,查询数据库,获取对应的 client_secret
    10$client_secret = getClientSecretById($client_id);
    11 
    12// 3、服务端重新生成一份 api_token
    13$api_token_server = md5($module $controller $action .  date('Y-m-d', time()) .  $client_secret);
    14 
    15// 4、客户端传过来的 api_token 与服务端生成的 api_token 进行校对,如果不相等,则表示验证失败
    16if ($api_token != $api_token_server) {
    17    exit('access deny');  // 拒绝访问
    18}
    19 
    20// 5、验证通过,返回数据给客户端
    21//。。。
    22?>

     

    再说第二个token(user_token)

     

    它的职责是保护用户的用户名及密码多次提交,以防密码泄露。

    如果接口需要用户登录,其访问流程如下:

    1、用户提交“用户名”和“密码”,实现登录(条件允许,这一步最好走https);

    2、登录成功后,服务端返回一个 user_token,生成规则参考如下:

    服务端用数据表维护user_token的状态,表设计如下:

    字段名字段类型注释
    user_idint用户ID
    user_tokenvarchar(36)用户token
    expire_timeint过期时间(Unix时间戳)

    (注:只列出了核心字段,其它的再扩展吧!!!)

    服务端生成 user_token 后,返回给客户端(自己存储),客户端每次接口请求时,如果接口需要用户登录才能访问,则需要把 user_id 与 user_token 传回给服务端,服务端接受到这2个参数后,需要做以下几步:

    1、检测 api_token的有效性;

    2、删除过期的 user_token 表记录;

    3、根据 user_id,user_token 获取表记录,如果表记录不存在,直接返回错误,如果记录存在,则进行下一步;

    4、更新 user_token 的过期时间(延期,保证其有效期内连续操作不掉线);

    5、返回接口数据;

     

    接口用例如下:

     

    1、发布日志

    URL:  http://blog.snsgou.com/blog/Index/addBlog?client_id=wt3734wy636dhd3636sr5858t6&api_token=880fed4ca2aabd20ae9a5dd774711de2&user_token=etye0fgkgk4ca2aabd20ae9a5dd77471fgf&user_id=12

    请求方式:  POST

    POST参数:title=我是标题&content=我是内容

    返回数据:

    {
          'code' => 1, // 1:成功 0:失败
          'msg' => '操作成功' // 登录失败、无权访问
          'data' => []
    }

    青砖黛瓦 故景如旧 草木无情 不解凡忧

    转载于:https://www.cnblogs.com/yaqiangyinsi/p/6130185.html

    展开全文
  • APP登录之access_token详解

    万次阅读 2019-09-19 14:36:20
    WEB开发,登录信息可以通过记录SESSION来进行验证,而在APP开发的过程中,由于无法保存SESSION,所以通过access_token来进行登录验证。如果用户有access_toke,并且正确,则验证通过,否则提示未登录。 操作步骤 ...
  • 登录账号得到的token信息。最好不要作为一个独立的单利对象存储;而是将它作为单例对象的属性userInfo,便于切换账号存储token和其他账号信息 如果之前是使用独立的单利对象UserInfoModel ,为了兼容代码可以这么做...
  • token

    2020-06-03 10:29:52
    Token 是在服务端产生的。如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回 Token 给前端。前端可以在每次请求的时候带上 Token 证明自己的合法地位 不久前,我在在前后端分离实践...
  • 移动应用开发过程中请求服务端采用token(在计算机身份认证中是令牌(临时))方式请求方式进行,get请求...,因为涉及敏感行业数据APP接口开发过程中安全性成为要求,在网上看了很多资料最后选择采用token+time+no...
  • PHP 写 APP 登录验证token接口 及 注册

    千次阅读 2018-07-10 10:27:52
     Response::json(404,"记录不存在");  }else if($data==false){  Response::json(406,"读取数据失败");  }else{  Response::json(500,"服务器发生错误");  }  }else{  Response::json(400,"用户名或手机号...
  • 1为了维护app用户的登录状态,我们可以利用token来实现。 2客户端输入账号密码,发起登录请求,服务端在登录接口验证通过后,给客户端返回一个任意字符串,既token,生成算法可随机,token必须与用户的账户关联
  • Token

    2019-07-08 19:25:00
    前端可以在每次请求的时候带上 Token 证明自己的合法地位。 不久前,我在前后端分离实践中提到了基于 Token 的认证,现在我们稍稍深入一些。 通常情况下,我们在讨论某个技术的时候,都是从问题开始。那么第一...
  • 什么是JSON Web Token? JSON Web Token(JWT)是一个开放式标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间以JSON对象安全传输信息。这些信息可以通过数字签名进行验证和信任。可以使用秘密...
  • 实现此功能的场景是在当下用户对手机APP体验要求高,并且相对安全前提的推动下诞生;当你下载了一个QQ,微信第一次进行了账号和密码的登录,你从此以后打开应用免去了你每日打开应用都要输入账号跟密码的痛苦过程,...
  • SpringBoot框架集成token实现登录校验功能(APP)

    千次阅读 热门讨论 2019-04-18 14:00:56
    公司新项目,需要做移动端(Android和IOS),登录模块,两个移动端人员提出用token来校验登录状态,一脸懵懵的,没做过,对于token的基本定义都模棱两可,然后查资料查查查,最终OK完成,写篇博客记录一下 ...
  • 之前登录后端是可以传给前端token,但是前端就是怎么都传不会来token,前端打包项目或使用postman都可以传入token,唯独pc浏览器模拟手机传不过来,检查发现前端用浏览器模拟器手机传入是会先请求域请求OPTIONS的...
  • app服务器错误 内容精选换一换调用接口出错后,将不会返回结果数据。调用方可根据每个接口对应的错误码来定位错误原因。当调用出错时,HTTP请求返回一个 4xx或5xx的HTTP状态码。返回的消息体中是具体的错误代码及...
  • App通过第三方提供的SDK可以获取到第三方的openId和第三方对应的access_token, 但是第三方提供的access_token只能访问第三方的接口,不能访问自己服务的接口。我们要做的就是根据第三方的openId来换取自己服务对应的...
  • 简单说一下App开发,App开发,是指专注于手机应用软件开发与服务。 App是application的缩写,通常专指手机上的应用软件,或称手机客户端。另外有很多在线app开发平台。 移动互联网时代是全民的移动互联网时代,是每...
  • token处理之一基本参数配置处理token时间、存储策略,客户端配置等 以前的都是spring security oauth默认的token生成策略,token默认在org.springframework.security.oauth2.provider.token.DefaultTokenServices ...
  • 文章目录介绍实现功能用户名密码登录步骤编写成功处理器配置成功处理器...介绍 ...登录成功之后返回token。但在app中,需要用户名和密码或者是手机号验证码登录成功之后也返回token。下面将对这两种模式登录成功之后返...
  • 在登录的过程中,使用token进行验证。 这里用到了RSA的加密传输,具体的加密方式,请参考这篇博客:【RSA加密】初探RSA并简单使用 ... CREATE TABLE `app_token_record` ( `member_id` int(11) NOT...
  • 一、客户端(APP)和服务端验签机制 1.背景  在以前传统项目中,当用户输入完正确的用户名和密码之后,服务端会在session存入用户的信息,即客户端登录成功后,服务端给其分配一个sessionId,返回给客户端,以后...
  • 手机APP登录方式,除了短信验证码登录之外,还可以绑定第三方社交账号进行登录,但是手机验证码登录是必不可少的一个环节,可以在后期客户维护起到至关重要的作用。 这次是从前端和后端给介绍第三方的短信验证码...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,892
精华内容 4,356
关键字:

手机app的token存在哪里