The Ins and Outs of Token Based Authentication

本文深入探讨了Token-based认证为何成为现代API和应用的主要选择,包括其优势、工作原理以及实现方式。它解释了与传统认证方法相比的改进,如状态无状态、易于扩展性、安全性提升等,并展示了如何通过Token实现跨平台和域的数据访问。

Introduction

Token based authentication is prominent everywhere on the web nowadays. With most every web company using an API, tokens are the best way to handle authentication for multiple users.

There are some very important factors when choosing token based authentication for your application. The main reasons for tokens are:

Stateless and scalable servers
Mobile application ready
Pass authentication to other applications
Extra security

# Who Uses Token Based Authentication?

Any major API or web application that you’ve come across has most likely used tokens. Applications like Facebook, Twitter, Google+, GitHub, and so many more use tokens.

Let’s take a look at exactly how it works.

# Why Tokens Came Around

Before we can see how token based authentication works and its benefits, we have to look at the way authentication has been done in the past.

Server Based Authentication (The Traditional Method)

Since the HTTP protocol is stateless, this means that if we authenticate a user with a username and password, then on the next request, our application won’t know who we are. We would have to authenticate again.

The traditional way of having our applications remember who we are is to store the user logged in information on the server. This can be done in a few different ways on the session, usually in memory or stored on the disk.

Here is a graph of how a server based authentication workflow would look:

tokens-traditional

As the web, applications, and the rise of the mobile application have come about, this method of authentication has shown problems, especially in scalability.

# The Problems with Server Based Authentication

A few major problems arose with this method of authentication.

Sessions: Every time a user is authenticated, the server will need to create a record somewhere on our server. This is usually done in memory and when there are many users authenticating, the overhead on your server increases.

Scalability: Since sessions are stored in memory, this provides problems with scalability. As our cloud providers start replicating servers to handle application load, having vital information in session memory will limit our ability to scale.

CORS: As we want to expand our application to let our data be used across multiple mobile devices, we have to worry about cross-origin resource sharing (CORS). When using AJAX calls to grab resources from another domain (mobile to our API server), we could run into problems with forbidden requests.

CSRF: We will also have protection against cross-site request forgery (CSRF). Users are susceptible to CSRF attacks since they can already be authenticated with say a banking site and this could be taken advantage of when visiting other sites.

With these problems, scalability being the main one, it made sense to try a different approach.

# How Token Based Works

Token based authentication is stateless. We are not storing any information about our user on the server or in a session.

This concept alone takes care of many of the problems with having to store information on the server.

No session information means your application can scale and add more machines as necessary without worrying about where a user is logged in.

Although this implementation can vary, the gist of it is as follows:

  1. User Requests Access with Username / Password
  2. Application validates credentials
  3. Application provides a signed token to the client
  4. Client stores that token and sends it along with every request
  5. Server verifies token and responds with data

Every single request will require the token. This token should be sent in the HTTP header so that we keep with the idea of stateless HTTP requests. We will also need to set our server to accept requests from all domains using Access-Control-Allow-Origin: *. What’s interesting about designating * in the ACAO header is that it does not allow requests to supply credentials like HTTP authentication, client-side SSL certificates, or cookies.

Here’s an infographic to explain the process:

tokens-new

Once we have authenticated with our information and we have our token, we are able to do many things with this token.

We could even create a permission based token and pass this along to a third-party application (say a new mobile app we want to use), and they will be able to have access to our data — but only the information that we allowed with that specific token.

# The Benefits of Tokens

Stateless and Scalable

infinity

Tokens stored on client side. Completely stateless, and ready to be scaled. Our load balancers are able to pass a user along to any of our servers since there is no state or session information anywhere.

If we were to keep session information on a user that was logged in, this would require us to keep sending that user to the same server that they logged in at (called session affinity).

This brings problems since, some users would be forced to the same server and this could bring about a spot of heavy traffic.

Not to worry though! Those problems are gone with tokens since the token itself holds the data for that user.

Security

you-shall-not-pass

The token, not a cookie, is sent on every request and since there is no cookie being sent, this helps to prevent CSRF attacks. Even if your specific implementation stores the token within a cookie on the client side, the cookie is merely a storage mechanism instead of an authentication one. There is no session based information to manipulate since we don’t have a session!

The token also expires after a set amount of time, so a user will be required to login once again. This helps us stay secure. There is also the concept of token revocation that allows us to invalidate a specific token and even a group of tokens based on the same authorization grant.

Extensibility (Friend of A Friend and Permissions)

share-candy

Tokens will allow us to build applications that share permissions with another. For example, we have linked random social accounts to our major ones like Facebook or Twitter.

When we login to Twitter through a service (let’s say Buffer), we are allowing Buffer to post to our Twitter stream.

By using tokens, this is how we provide selective permissions to third-party applications. We could even build our own API and hand out special permission tokens if our users wanted to give access to their data to another application.

Multiple Platforms and Domains

We talked a bit about CORS earlier. When our application and service expands, we will need to be providing access to all sorts of devices and applications (since our app will most definitely become popular!).

Having our API just serve data, we can also make the design choice to serve assets from a CDN. This eliminates the issues that CORS brings up after we set a quick header configuration for our application.


Access-Control-Allow-Origin: *

Our data and resources are available to requests from any domain now as long as a user has a valid token.

Standards Based

When creating the token, you have a few options. We’ll be diving more into this topic when we secure an API in a follow-up article, but the standard to use would be JSON Web Tokens.

This handy debugger and library chart shows the support for JSON Web Tokens. You can see that it has a great amount of support across a variety of languages. This means you could actually switch out your authentication mechanism if you choose to do so in the future!

# Conclusion

This was just a look at the how and why of token based authentication. As is always the case in the world of security, there is much, much, much, much (too many?) more to each topic and it varies per use case. We even dove into some topics on scalability which deserves its own conversation as well.

This was a high level quick overview, so please feel free to point out anything that was missed or any questions you have on the matter.

In our next article, we’ll be looking at the anatomy of JSON Web Tokens. For full code examples on how to authenticate a Node API using JSON Web Tokens, check out our book MEAN Machine.

Edit #1 Adding ACAO header info and CSRF clarifications (thanks to Emily Stark for the article info)
<think>我们正在处理一个错误:"Cannot instantiate the type AuthenticationToken" 这个错误通常发生在Java中,当我们尝试实例化一个接口或抽象类时。因为接口和抽象类不能被直接实例化,我们必须使用它们的实现类或子类。 假设我们正在使用一个安全框架(如Apache Shiro或Spring Security),其中AuthenticationToken是一个接口。 例如,在Shiro中,org.apache.shiro.authc.AuthenticationToken是一个接口,我们通常使用它的实现类,如UsernamePasswordToken。 解决方案: 1. 检查AuthenticationToken的来源(哪个包,哪个框架)。 2. 使用该接口的一个具体实现类,而不是直接实例化接口。 示例代码(以Apache Shiro为例): 错误的方式: AuthenticationToken token = new AuthenticationToken(); // 错误,因为接口不能被实例化 正确的方式: 使用UsernamePasswordToken(假设我们使用用户名和密码认证): AuthenticationToken token = new UsernamePasswordToken(username, password); 下面是一个使用Shiro的简单示例: 步骤: 1. 创建Subject实例。 2. 创建令牌(使用UsernamePasswordToken)。 3. 登录(调用subject.login(token))。 代码示例: ```java import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; public class ShiroLoginExample { public static void main(String[] args) { // 获取当前Subject Subject currentUser = SecurityUtils.getSubject(); // 假设从前端获取的用户名和密码 String username = "user123"; String password = "secret"; // 创建令牌,这里使用UsernamePasswordToken,它是AuthenticationToken的一个实现 AuthenticationToken token = new UsernamePasswordToken(username, password); try { // 登录(调用Subject的login方法) currentUser.login(token); System.out.println("登录成功!"); } catch (Exception e) { // 登录失败处理 System.out.println("登录失败: " + e.getMessage()); } } } ``` 解释: - 我们通过`SecurityUtils.getSubject()`获取当前Subject(用户)。 - 然后创建了一个`UsernamePasswordToken`对象,它封装了用户名和密码。注意,这里我们使用的是具体的实现类,而不是直接实例化接口。 - 调用`currentUser.login(token)`进行登录。如果登录失败,会抛出异常。 注意事项: - 确保项目中已经引入了Shiro的相关依赖(如Maven依赖)。 - 需要配置Shiro的安全管理器(SecurityManager)和Realm(数据源),这些配置通常在单独的配置文件中(如shiro.ini)或通过Spring配置。 如果使用的是其他安全框架(如Spring Security),那么处理方式会有所不同。在Spring Security中,通常使用`UsernamePasswordAuthenticationToken`(注意类名不同)作为认证令牌。 例如,在Spring Security中: ```java import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; // 创建令牌 Authentication authentication = new UsernamePasswordAuthenticationToken(username, password); ``` 因此,解决“Cannot instantiate the type AuthenticationToken”的关键是: - 确定你使用的安全框架。 - 找到AuthenticationToken接口的具体实现类,并使用该实现类创建对象。 如果问题仍然存在,请检查: - 导入的包是否正确(是否导入了正确的AuthenticationToken接口)。 - 是否混淆了不同框架的类(例如,错误地混合了Shiro和Spring Security的类)。 最后,根据你的具体框架和需求,选择正确的实现类。 §§相关问题§§: 1. 如何在Apache Shiro中配置Realm? 2. 在Spring Security中,AuthenticationToken是如何使用的? 3. 如何自定义一个AuthenticationToken实现? 4. 如何处理认证过程中的异常?</think>### 解决 "Cannot instantiate the type AuthenticationToken" 错误 这个错误发生在 Java 中尝试实例化一个 **接口或抽象类** 时。`AuthenticationToken` 通常是安全框架(如 Apache Shiro 或 Spring Security)中的接口,不能被直接实例化。你需要使用它的具体实现类。 --- ### 解决方案与代码示例 #### 1. **使用框架提供的实现类(以 Apache Shiro 为例)** ```java import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.UsernamePasswordToken; public class AuthExample { public static void main(String[] args) { String username = "admin"; String password = "secret"; // ✅ 正确:使用具体实现类 UsernamePasswordToken AuthenticationToken token = new UsernamePasswordToken(username, password); // 后续认证逻辑(例如 subject.login(token)) System.out.println("Token created: " + token.getPrincipal()); } } ``` **关键点**: - `UsernamePasswordToken` 是 Shiro 中 `AuthenticationToken` 的标准实现。 - 通过实现类构造对象,而非直接实例化接口。 --- #### 2. **自定义实现类(适用于需要扩展的场景)** ```java import org.apache.shiro.authc.AuthenticationToken; // ✅ 自定义实现类 public class CustomToken implements AuthenticationToken { private final String username; private final String credential; public CustomToken(String username, String credential) { this.username = username; this.credential = credential; } @Override public Object getPrincipal() { return username; } @Override public Object getCredentials() { return credential; } } // 使用自定义类 public class Main { public static void main(String[] args) { AuthenticationToken token = new CustomToken("user", "pass123"); System.out.println("Custom token: " + token.getPrincipal()); } } ``` **关键点**: - 实现 `AuthenticationToken` 接口并重写 `getPrincipal()` 和 `getCredentials()` 方法。 - 使用自定义类实例化对象。 --- ### 错误原因分析 ```java // ❌ 错误示例:直接实例化接口 AuthenticationToken token = new AuthenticationToken(); // 编译错误 ``` - **接口不能实例化**:`AuthenticationToken` 是一个接口,需要具体实现类。 - **抽象类同理**:如果它是抽象类,也需要子类实现。 --- ### 各框架的实现类参考 | 框架 | 接口 | 常用实现类 | |------------------|--------------------------|-----------------------------------| | **Apache Shiro** | `AuthenticationToken` | `UsernamePasswordToken` | | **Spring Security** | `Authentication` | `UsernamePasswordAuthenticationToken` | | **自定义实现** | `AuthenticationToken` | 自行实现接口的类 | --- ### 最佳实践 1. **检查文档**:确认你使用的安全框架(Shiro/Security/JWT 等)。 2. **使用内置类**:优先选择框架提供的标准实现类。 3. **依赖注入**:在 Spring 中通过 Bean 管理认证对象。 4. **避免 `new` 接口**:始终实例化具体类而非接口或抽象类。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值