当前位置: 首页 > 编程日记 > 正文

如何设置Java Spring Boot JWT授权和认证

In the past month, I had a chance to implement JWT auth for a side project. I have previously worked with JWT in Ruby on Rails, but this was my first time in Spring.

在过去的一个月中,我有机会为辅助项目实现JWT auth。 我以前曾在Ruby on Rails中使用JWT,但这是我第一次在Spring。

In this post, I will try to explain what I have learned and applied in my project to share my experience and hopefully help some people.

在这篇文章中,我将尝试解释我在项目中学到的知识和应用的知识,以分享我的经验,并希望能对一些人有所帮助。

We will start by taking a quick look at the theory behind JWT and how it works. Then we will look at how to implement it in a Spring Boot application.

我们将快速浏览一下JWT背后的理论及其工作原理。 然后,我们将研究如何在Spring Boot应用程序中实现它。

JWT基础 (JWT Basics)

JWT, or JSON Web Tokens (RFC 7519), is a standard that is mostly used for securing REST APIs. Despite being a relatively new technology, it is gaining rapid popularity.

JWT或JSON Web令牌( RFC 7519 )是一种标准,主要用于保护REST API。 尽管是相对较新的技术,但它正在Swift普及。

In the JWT auth process, the front end (client) firstly sends some credentials to authenticate itself (username and password in our case, since we're working on a web application).

在JWT身份验证过程中,前端(客户端)首先发送一些凭据以对其自身进行身份验证(在本例中为用户名和密码,因为我们正在处理Web应用程序)。

The server (the Spring app in our case) then checks those credentials, and if they are valid, it generates a JWT and returns it.

然后,服务器(在我们的示例中为Spring应用程序)检查这些凭据,如果它们有效,它将生成一个JWT并将其返回。

After this step client has to provide this token in the request’s Authorization header in the “Bearer TOKEN” form. The back end will check the validity of this token and authorize or reject requests. The token may also store user roles and authorize the requests based on the given authorities.

此步骤之后,客户端必须以“ Bearer TOKEN”形式在请求的Authorization标头中提供此令牌。 后端将检查此令牌的有效性并授权或拒绝请求。 令牌还可以存储用户角色并根据给定的权限授权请求。

实作 (Implementation)

Now let’s see how we can implement the JWT login and save mechanism in a real Spring application.

现在,让我们看看如何在真正的Spring应用程序中实现JWT登录和保存机制。

依存关系 (Dependencies)

You can see the list of Maven dependencies that our example code uses below. Note that the core dependencies like Spring Boot and Hibernate are not included in this screenshot.

您可以在下面查看我们的示例代码使用的Maven依赖项列表。 请注意,此屏幕快照中未包括诸如Spring Boot和Hibernate之类的核心依赖项。

保存用户 (Saving Users)

We will start by creating controllers to save users securely and authenticate them based on username and password.

我们将首先创建控制器以安全地保存用户并根据用户名和密码对他们进行身份验证。

We have a model entity called User. It is a simple entity class that maps to the USER table. You can use whatever properties you need depending on your application.

我们有一个称为用户的模型实体。 这是一个映射到USER表的简单实体类。 您可以根据应用程序使用所需的任何属性。

We also have a simple UserRepository class to save users. We need to override the findByUsername method since we will use it in authentication.

我们还有一个简单的UserRepository类来保存用户。 我们需要重写findByUsername方法,因为我们将在身份验证中使用它。

public interface UserRepository extends JpaRepository<User, String>{     User findByUsername(String username); }

We should never store plaintext passwords in the database because many users tend to use the same password for multiple sites.

我们绝不应该在数据库中存储纯文本密码,因为许多用户倾向于对多个站点使用相同的密码。

There are many different hashing algorithms, but the most commonly used one is BCrypt and it is a recommended method of secure hashing. You can check out this article for more information on the topic.

哈希算法有许多种,但是最常用的算法是BCrypt ,它是推荐的安全哈希方法。 你可以看看这个文章的主题的更多信息。

@Bean public BCryptPasswordEncoder bCryptPasswordEncoder() {    return new BCryptPasswordEncoder(); }

To hash the password, we will define a BCrypt bean in @SpringBootApplication and annotate the main class as follows:

为了哈希密码,我们将在@SpringBootApplication中定义一个BCrypt bean并注释主类,如下所示:

We will call the methods on this bean when we need to hash a password.

需要哈希密码时,将在此bean上调用方法。

We also need a UserController to save users. We create the controller, annotate it with @RestController, and define the corresponding mapping.

我们还需要一个UserController来保存用户。 我们创建控制器,使用@RestController对其进行注释,然后定义相应的映射。

In our application, we save the user based on a DTO object that is passed from the front end. You can also pass a User object in @RequestBody.

在我们的应用程序中,我们基于从前端传递的DTO对象保存用户。 您还可以在@RequestBody中传递一个User对象。

After we pass the DTO object, we encrypt the password field using the BCrypt bean we created earlier. You could also do this in the controller, but it is a better practice to put this logic in the service class.

传递DTO对象之后,我们使用之前创建的BCrypt bean加密密码字段。 您也可以在控制器中执行此操作,但是最好将此逻辑放在服务类中。

@Transactional(rollbackFor = Exception.class)
public String saveDto(UserDto userDto) {            userDto.setPassword(bCryptPasswordEncoder.encode(userDto.getPassword()));return save(new User(userDto)).getId(); 
}

认证过滤器 (Authentication Filter)

We need authentication to make sure that the user is really who they claim to be. We will be using the classic username/password pair to accomplish this.

我们需要身份验证以确保用户确实是他们声称的真实身份。 我们将使用经典的用户名/密码对完成此操作。

Here are the steps to implement authentication:

以下是实现身份验证的步骤:

  1. Create our Authentication Filter that extends UsernamePasswordAuthenticationFilter

    创建扩展UsernamePasswordAuthenticationFilter的身份验证过滤器

  2. Create a security configuration class that extends WebSecurityConfigurerAdapter and apply the filter

    创建扩展WebSecurityConfigurerAdapter的安全配置类并应用过滤器

Here is the code for our Authentication Filter – as you might know, filters are the backbone of Spring Security.

这是我们的身份验证过滤器的代码–您可能知道,过滤器是Spring Security的基础。

Let’s go over this code step by step.

让我们逐步看一下这段代码。

This class extends UsernamePasswordAuthenticationFilter which is the default class for password authentication in Spring Security. We extend it to define our custom authentication logic.

此类扩展了UsernamePasswordAuthenticationFilter ,后者是Spring Security中用于密码认证的默认类。 我们对其进行扩展以定义我们的自定义身份验证逻辑。

We make a call to the setFilterProcessesUrl method in our constructor. This method sets the default login URL to the provided parameter.

我们在构造函数中调用setFilterProcessesUrl方法。 此方法将默认登录URL设置为提供的参数。

If you remove this line, Spring Security creates the “/login” endpoint by default. It defines the login endpoint for us, which is why we will not define a login endpoint in our controller explicitly.

如果删除此行,Spring Security默认会创建“ / login”端点。 它为我们定义了登录端点,这就是为什么我们不会在控制器中显式定义登录端点的原因。

After this line our login endpoint will be /api/services/controller/user/login. You can use this function to stay consistent with your endpoints.

在此行之后,我们的登录端点将是/ api / services / controller / user / login 。 您可以使用此功能与端点保持一致。

We override the attemptAuthentication and successfulAuthentication methods of the UsernameAuthenticationFilter class.

我们覆盖UsernameAuthenticationFilter类的attemptAuthenticationsuccessfulAuthentication方法。

The attemptAuthentication function runs when the user tries to log in to our application. It reads the credentials, creates a user POJO from them, and then checks the credentials to authenticate.

当用户尝试登录我们的应用程序时,将运行tryAuthentication函数。 它读取凭据,从凭据创建用户POJO,然后检查凭据以进行身份​​验证。

We pass the username, password, and an empty list. The empty list represents the authorities (roles), and we leave it as is since we do not have any roles in our application yet.

我们传递用户名,密码和一个空列表。 空列表代表权限(角色),由于我们在应用程序中还没有任何角色,因此我们将其保留为原样。

If the authentication is successful, the successfulAuthentication method runs. The parameters of this method are passed by Spring Security behind the scenes.

如果身份验证成功,则运行successAuthentication方法。 Spring Security在后台传递了此方法的参数。

The attemptAuthentication method returns an Authentication object that contains the authorities we passed while attempting.

tryAuthentication方法返回一个Authentication对象,其中包含我们在尝试时传递的权限。

We want to return a token to user after authentication is successful, so we create the token using username, secret, and expiration date. We need to define the SECRET and EXPIRATION_DATE now.

我们希望在身份验证成功后将令牌返回给用户,因此我们使用用户名,机密和有效期创建令牌。 我们现在需要定义SECRETEXPIRATION_DATE

We create a class to be a container for our constants. You can set the secret to whatever you want, but the best practice is making the secret key as long as your hash. We use the HS256 algorithm in this example, so our secret key is 256 bits/32 chars.

我们创建一个类作为常量的容器。 您可以将密钥设置为所需的任意值,但是最佳实践是只要您的哈希值都是密钥。 在此示例中,我们使用HS256算法,因此我们的密钥为256位/ 32个字符。

The expiration time is set to 15 minutes, because it is the best practice against secret key brute-forcing attacks. The time is in milliseconds.

到期时间设置为15分钟,因为这是对付秘密密钥强行攻击的最佳实践。 时间以毫秒为单位。

We have prepared our Authentication filter, but it is not active yet. We also need an Authorization filter, and then we will apply them both through a configuration class.

我们已经准备好身份验证过滤器,但是它尚未激活。 我们还需要一个授权过滤器,然后我们将通过配置类将它们都应用。

This filter will check the existence and validity of the access token on the Authorization header. We will specify which endpoints will be subject to this filter in our configuration class.

该过滤器将检查授权标头上访问令牌的存在和有效性。 我们将在我们的配置类中指定哪些端点将受此过滤器的约束。

授权过滤器 (Authorization Filter)

The doFilterInternal method intercepts the requests then checks the Authorization header. If the header is not present or doesn’t start with “BEARER”, it proceeds to the filter chain.

doFilterInternal方法拦截请求,然后检查Authorization标头。 如果标题不存在或不以“ BEARER”开头,则它前进到过滤器链。

If the header is present, the getAuthentication method is invoked. getAuthentication verifies the JWT, and if the token is valid, it returns an access token which Spring will use internally.

如果存在标头,则调用getAuthentication方法。 getAuthentication验证JWT,如果令牌有效,它将返回Spring将在内部使用的访问令牌。

This new token is then saved to SecurityContext. You can also pass in Authorities to this token if you need for role-based authorization.

然后,此新令牌将保存到SecurityContext。 如果需要基于角色的授权,也可以将“权限”传递给此令牌。

Our filters are ready, and now we need to put them into action with the help of a configuration class.

我们的过滤器已经准备就绪,现在我们需要在配置类的帮助下将其付诸实践。

组态 (Configuration)

We annotate this class with @EnableWebSecurity and extend WebSecurityConfigureAdapter to implement our custom security logic.

我们使用@EnableWebSecurity注释此类,并扩展WebSecurityConfigureAdapter来实现我们的自定义安全逻辑。

We autowire the BCrypt bean that we defined earlier. We also autowire the UserDetailsService to find the user’s account.

我们自动连接我们先前定义的BCrypt bean。 我们还将自动连接UserDetailsS​​ervice以查找用户的帐户。

The most important method is the one which accepts an HttpSecurity object. Here we specify the secure endpoints and filters that we want to apply. We configure CORS, and then we permit all post requests to our sign up URL that we defined in the constants class.

最重要的方法是接受HttpSecurity对象的方法。 在这里,我们指定我们要应用的安全端点和过滤器。 我们配置CORS,然后允许所有发布请求到我们在常量类中定义的注册URL。

You can add other ant matchers to filter based on URL patterns and roles, and you can check this StackOverflow question for examples regarding that. The other method configures the AuthenticationManager to use our encoder object as its password encoder while checking the credentials.

您可以添加其他蚂蚁匹配器以根据URL模式和角色进行过滤,并且可以查看此StackOverflow问题以获取有关此示例的示例。 另一种方法将AuthenticationManager配置为在检查凭据时将我们的编码器对象用作其密码编码器。

测试中 (Testing)

Let’s send a few requests to test if it works properly.

让我们发送一些请求以测试其是否正常运行。

Here we send a GET request to access a protected resource. Our server responds with a 403 code. This is the expected behavior because we haven’t provided a token in the header. Now let’s create a user:

在这里,我们发送GET请求以访问受保护的资源。 我们的服务器以403代码响应。 这是预期的行为,因为我们没有在标头中提供令牌。 现在让我们创建一个用户:

To create a user, we send a post request with our User DTO data. We will use this user to login and get an access token.

要创建用户,我们发送带有用户DTO数据的发帖请求。 我们将使用该用户登录并获得访问令牌。

Great! We got the token. After this point, we will use this token to access protected resources.

大! 我们得到了令牌。 此后,我们将使用此令牌访问受保护的资源。

We provide the token in the Authorization header and we are now allowed access to our protected endpoint.

我们在Authorization标头中提供令牌,现在可以访问受保护的端点了。

结论 (Conclusion)

In this tutorial I have walked you through the steps I took when implementing JWT authorization and password authentication in Spring. We also learned how to save a user securely.

在本教程中,我向您介绍了在Spring中实现JWT授权和密码身份验证时所采取的步骤。 我们还学习了如何安全地保存用户。

Thank you for reading – I hope it was helpful to you. If you are interested in reading more content like this, feel free to subscribe to my blog at https://erinc.io. :)

感谢您的阅读-希望对您有所帮助。 如果您有兴趣此类内容,请随时通过https://erinc.io订阅我的博客。 :)

翻译自: https://www.freecodecamp.org/news/how-to-setup-jwt-authorization-and-authentication-in-spring/

相关文章:

算法(5)哈希表

1.0 问题描述 实现数据结构&#xff1a;哈希表。 2.0 问题分析 哈希表可以看作我们经常使用的字典&#xff08;swift&#xff09;或对象&#xff08;js&#xff09;&#xff0c;可以让一个key&value对一一对应&#xff0c;可以快速根据key找到value。哈希表内部使用数组…

《面向对象程序设计》c++第五次作业___calculator plus plus

c第五次作业 Calculator plusplus 代码传送门 PS:这次作业仍然orz感谢一位同学与一位学长的windows帮助&#xff0c;同时再次吐槽作业对Mac系统用户的不友好。&#xff08;没朋友千万别用Mac&#xff01;&#xff01;&#xff01;&#xff09; 还有想吐槽作业对规范的要求大大超…

联合体union和大小端(big-endian、little-endian)

1.联合体union的基本特性——和struct的同与不同union&#xff0c;中文名“联合体、共用体”&#xff0c;在某种程度上类似结构体struct的一种数据结构&#xff0c;共用体(union)和结构体(struct)同样可以包含很多种数据类型和变量。在成员完全相同的情况下&#xff0c;struct比…

前端面试的作品示例_如何回答任何技术面试问题-包括示例

前端面试的作品示例Technical interviews can be extremely daunting. From the beginning of each question to the end, its important to know what to expect, and to be aware of the areas you might be asked about. 技术面试可能会非常艰巨。 从每个问题的开始到结束&a…

$(shell expr $(MAKE_VERSION) \= 3.81) 这里“\”的解释

android/build/core/main.mk $(shell expr $(MAKE_VERSION) \> 3.81) 为什么要加多一个“\”,因为">"会被shell解析为重定向符号&#xff0c;所以需要转义或用引号包围 所以&#xff0c;也可以这样写$(shell expr $(MAKE_VERSION) “>” 3.81)转载于:https:…

iOS应用模块化的思考及落地方案(一)模块的划分及模块化工作流程

1.0 什么是模块化 很多关于重构及设计模式的介绍中&#xff0c;经常提到的几个词语是复用及解耦。 模块化之所以被提出&#xff0c;也更多是为了解决这几个问题。 复用可以减少重复造轮子的情况&#xff0c;很容易理解的是&#xff0c;我们经常使用的github上的第三方框架&a…

Swiper 用法

部分常用API ininialSlide: 2, //起始图片切换的索引位置&#xff08;起始从0开始&#xff0c;默认为0&#xff09; autoplay: 3000, //设置自动切换时间&#xff0c;单位毫秒 speed: 1000, //设置滑动速度 continuous: true, //无限循环的图片切换效果 disableScroll: true, /…

node/js 漏洞_6个可用于检查Node.js中漏洞的工具

node/js 漏洞Vulnerabilities can exist in all products. The larger your software grows, the greater the potential for vulnerabilities. 所有产品中都可能存在漏洞。 您的软件增长得越大&#xff0c;潜在的漏洞就越大。 Vulnerabilities create opportunities for expl…

发现一个浏览器很奇怪的问题

浏览器有8个请求状态为pending时&#xff0c;在另一个tab中&#xff0c;请求就发布出去了&#xff0c;一直是stalled。直到pending状态变成了cancled状态。 试了360浏览器&#xff08;谷歌内核&#xff09;和chrome浏览器&#xff0c;都是这样。 具体的原因待深究 参考&#xf…

wamp配置虚拟主机

因为wampserver的php版本一直是5.x版本&#xff1b;因此转投xmapp用了一段时间&#xff1b; 意外发现wampserver3更新了&#xff1b;php也终于更新到7了&#xff1b; 果断还是决定回到wampserver的怀抱&#xff1b; 然后有意外的发现了wampserver3有了新功能&#xff1b;可以方…

iOS应用模块化的思考及落地方案(二)模块化自动构建工具的使用

1.0 iOS模块化中的问题 前文已经介绍了模块化的流程及一些常见的问题&#xff0c;我们在这里再次总结一下。 在工作中&#xff0c;当我们开始一个新项目的时候&#xff0c;最先考虑的就是模块化工作。 模块化工作的想法是很美好的&#xff0c;可是执行过程中会遇到很多的问题…

aws fargate_我如何在AWS Fargate上部署#100DaysOfCloud Twitter Bot

aws fargateAfter passing my last certification, I asked myself how much time I spent studying cloud computing.通过上一份认证后&#xff0c;我问自己自己花了多少时间研究云计算。 More than 100 days!超过100天&#xff01; It also made me realize two things:这也…

think in Java 第五章之垃圾回收类型

1.引用计数&#xff1a; 每个对象都含有一个引用计数器&#xff0c;当有引用连接至对象时&#xff0c;引用计数加1&#xff0c;当引用离开作用域或被置为null时&#xff0c;引用计数减1. 缺陷&#xff1a;在对象循环引用时&#xff0c;存在“对象应该被回收&#xff0c;引用计数…

Yii 错误页面处理

【错误页面处理】 訪问一个错误的控制器 訪问一个错误的方法 有些控制器和方法禁止訪问 以上訪问会提示错误信息 404 403 以上错误信息是不方便给外边用户看到的。 1. 安全隐患 2. 用户体验不好 错误信息在site/error这个地方定义的。如今我们要自己定义错误页面来显示我们的错…

设置RGBColor

#define kUIColorFromRGB(rgbValue) [UIColor \colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]

自学成才翁_作为一名自学成才的开发者从“我的旅程”中吸取的教训

自学成才翁The path of the self-taught developer is tough and filled with uncertainty. There is no straight line from newbie to career programmer. Because of this, I believe all self-taught developers have a unique story to tell.自学成才的开发者之路艰难而充…

67)vector的begin() end() 和 front() back()的区别 rbegin() rend()

1&#xff09; 2&#xff09;v1.begin() 和v1.end&#xff08;&#xff09; 是作为迭代器v1的 第一个位置 和 最后一个元素的下一个位置。 v1.front() 是v1这个动态数组的第一个元素的值 v1.back()是v1的最后一个元素的值。 3&#xff09; 4&#xff09;正向和反向的使…

倒置函数reverse的用法

倒置字符串函数reverse&#xff1a;用于倒置字符串s中的各个字符的位置&#xff0c;如原来字符串中如果初始值为123456&#xff0c;则通过reverse函数可将其倒置为654321&#xff0c;程序如下&#xff1a;#include<stdio.h>#include<string.h>void reverse(char s[…

设置tabbaritem的title的颜色及按钮图片

设置title颜色&#xff1a; [[UITabBarItem appearance] setTitleTextAttributes:{NSForegroundColorAttributeName : kUIColorFromRGB(0xb2151c)} forState:UIControlStateSelected]; 设置按钮图片&#xff1a; UIImage *commonImage [UIImage imageNamed:[NSString strin…

helm部署仓库中没有的包_Kubernetes的Helm软件包管理器简介

helm部署仓库中没有的包Before we dive into the Helm package manager, Im going to explain some key concepts to deploying any application anywhere. Ill also give you a brief introduction to Kubernetes terminology.在深入研究Helm软件包管理器之前 &#xff0c;我将…

mem 族函数的实现

1.void * memcpy ( void * dest, const void * src, size_t num ); 头文件&#xff1a;#include <string.h>memcpy() 用来复制内存&#xff0c;memcpy() 会复制 src 所指的内存内容的前 num 个字节到 dest 所指的内存地址上。memcpy() 并不关心被复制的数据类型&#xff…

快排递归非递归python_Python递归神经网络终极指南

快排递归非递归pythonRecurrent neural networks are deep learning models that are typically used to solve time series problems. They are used in self-driving cars, high-frequency trading algorithms, and other real-world applications.循环神经网络是深度学习模型…

我的hadoop学习之路

Hadoop实现了一个分布式文件系统&#xff08;Hadoop Distributed File System&#xff09;&#xff0c;简称HDFS。HDFS有高容错性的特点&#xff0c;并且设计用来部署在低廉的&#xff08;low-cost&#xff09;硬件上。 Hadoop的框架最核心的设计就是&#xff1a;HDFS和MapRedu…

日期处理工具类 -【二】

1、返回本周的第一天(周日为每周第一天) 1 /**2 * 返回本周的第一天(周日为每周第一天)3 * return4 */5 public static String getTheFirstDayOfThisWeek(){6 SimpleDateFormat format new SimpleDateFormat("yyyy-MM-dd");7 Calendar cal Calendar.get…

超越对手pdf_如何创建一个超越竞争对手的移动应用

超越对手pdfThe amount of time people spend on their mobile phones has increased over the years, and so has the number of people using mobile devices.多年来&#xff0c;人们在手机上花费的时间增加了&#xff0c;使用移动设备的人数也增加了。 It’s safe to say t…

vue路由对象($route)参数简介

路由对象在使用了 vue-router 的应用中&#xff0c;路由对象会被注入每个组件中&#xff0c;赋值为 this.$route &#xff0c;并且当路由切换时&#xff0c;路由对象会被更新。 so , 路由对象暴露了以下属性&#xff1a; 1.$route.path 字符串&#xff0c;等于当前路由对象的路…

join......on 后面的and 和where的区别

a.where 是在两个表join完成后&#xff0c;再附上where条件。   b. and 则是在表连接前过滤A表或B表里面哪些记录符合连接条件&#xff0c;同时会兼顾是left join还是right join。即   假如是左连接的话&#xff0c;如果左边表的某条记录不符合连接条件&#xff0c;那么它不…

block的运用

cell的.h文件 typedef void(^ActivityCellBlock)(NSString *str); interface ActivityCell : UITableViewCell property (nonatomic,strong) NSArray *labelAry; property (nonatomic,copy) ActivityCellBlock myBlock; -(void)showCell:(ActivityCellBlock)myBlock; cel…

如何在Ubuntu 20.04上设置Python虚拟环境

I recently got myself a “new” laptop – a Lenovo x270 (yay)! And once again I needed to set up a Python virtual environment. So of course I Googled for a solution, just to find my previously written article on the same topic!我最近给自己买了一台“新”笔记…

getURLParameters - 网址参数

返回包含当前URL参数的对象。 通过适当的正则表达式&#xff0c;使用 String.match() 来获得所有的键值对&#xff0c; Array.reduce() 来映射和组合成一个单一的对象。 将 location.search 作为参数传递给当前 url。 const getURLParameters url >url.match(/([^?&])…