Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $YECBGYFECGEAFWHA as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2

Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $BBWFDDBHHYHDXXAB as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2
Spring Security 与 JWT_创想鸟

Spring Security 与 JWT

spring security 与 jwt

在本文中,我们将探讨如何将 spring security 与 jwt 集成,为您的应用程序构建坚实的安全层。我们将完成从基本配置到实现自定义身份验证过滤器的每个步骤,确保您拥有必要的工具来高效、大规模地保护您的 api。

配置

在 spring initializr 中,我们将使用 java 21mavenjar 和这些依赖项构建一个项目:

spring 数据 jpa春天网龙目岛春季安全postgresql 驱动程序oauth2 资源服务器

设置 postgresql 数据库

使用 docker,您将使用 docker-compose 创建一个 postgresql 数据库。
在项目的根目录创建一个 docker-compose.yaml 文件。

services:  postgre:    image: postgres:latest    ports:      - "5432:5432"    environment:      - postgres_db=database      - postgres_user=admin      - postgres_password=admin    volumes:      - postgres_data:/var/lib/postgresql/datavolumes:  postgres_data:

运行命令启动容器。

docker compose up -d

设置 application.properties 文件

这个文件是spring boot应用程序的配置。

spring.datasource.url=jdbc:postgresql://localhost:5432/databasespring.datasource.username=adminspring.datasource.password=adminspring.jpa.hibernate.ddl-auto=updatespring.jpa.show-sql=truejwt.public.key=classpath:public.keyjwt.private.key=classpath:private.key

jwt.public.key 和 jwt.private.key 是我们将进一步创建的密钥。

生成私钥和公钥

永远不要将这些密钥提交到你的github

在控制台运行,在资源目录下生成私钥

cd src/main/resourcesopenssl genrsa > private.key

之后,创建链接到私钥的公钥。

openssl rsa -in private.key -pubout -out public.key 

代码

创建安全配置文件

靠近主函数创建一个目录 configs 并在其中创建一个 securityconfig.java 文件。

import java.security.interfaces.rsaprivatekey;import java.security.interfaces.rsapublickey;import org.springframework.beans.factory.annotation.value;import org.springframework.context.annotation.bean;import org.springframework.context.annotation.configuration;import org.springframework.http.httpmethod;import org.springframework.security.config.annotation.web.builders.httpsecurity;import org.springframework.security.config.annotation.web.configuration.enablewebsecurity;import org.springframework.security.crypto.bcrypt.bcryptpasswordencoder;import org.springframework.security.oauth2.jwt.jwtdecoder;import org.springframework.security.oauth2.jwt.jwtencoder;import org.springframework.security.oauth2.jwt.nimbusjwtdecoder;import org.springframework.security.oauth2.jwt.nimbusjwtencoder;import org.springframework.security.web.securityfilterchain;import com.nimbusds.jose.jwk.jwkset;import com.nimbusds.jose.jwk.rsakey;import com.nimbusds.jose.jwk.source.immutablejwkset;@configuration@enablewebsecurity@enablemethodsecuritypublic class securityconfig {    @value("${jwt.public.key}")    private rsapublickey publickey;    @value("${jwt.private.key}")    private rsaprivatekey privatekey;    @bean    securityfilterchain securityfilterchain(httpsecurity http) throws exception {        http                .csrf(csrf -> csrf.disable())                .authorizehttprequests(auth -> auth.requestmatchers(httpmethod.post, "/signin").permitall()                        .requestmatchers(httpmethod.post, "/login").permitall()                        .anyrequest().authenticated())                .oauth2resourceserver(config -> config.jwt(jwt -> jwt.decoder(jwtdecoder())));        return http.build();    }    @bean    bcryptpasswordencoder bpasswordencoder() {        return new bcryptpasswordencoder();    }    @bean    jwtencoder jwtencoder() {        var jwk = new rsakey.builder(this.publickey).privatekey(this.privatekey).build();        var jwks = new immutablejwkset(new jwkset(jwk));        return new nimbusjwtencoder(jwks);    }    @bean    jwtdecoder jwtdecoder() {        return nimbusjwtdecoder.withpublickey(publickey).build();    }}

解释

@enablewebscurity:当您使用@enablewebsecurity时,它会自动触发spring security的配置以保护web应用程序。此配置包括设置过滤器、保护端点以及应用各种安全规则。

@enablemethodsecurity:是 spring security 中的一个注释,可在 spring 应用程序中启用方法级安全性。它允许您使用 @preauthorize、@postauthorize、@secured 和 @rolesallowed 等注释直接在方法级别应用安全规则。

jQuery与CSS3选择滑块按钮代码 jQuery与CSS3选择滑块按钮代码

jQuery与CSS3选择滑块按钮代码

jQuery与CSS3选择滑块按钮代码 29 查看详情 jQuery与CSS3选择滑块按钮代码

privatekeypublickey:是用于签名和验证 jwt 的 rsa 公钥和私钥。 @value 注解将属性文件(application.properties)中的键注入到这些字段中。

csrf:禁用 csrf(跨站请求伪造)保护,该保护通常在使用 jwt 进行身份验证的无状态 rest api 中禁用。

authorizehttprequests:配置基于url的授权规则。

requestmatchers(httpmethod.post, “/signin”).permitall():允许未经身份验证访问 /signin 和 /login 端点,这意味着任何人都可以在不登录的情况下访问这些路由。anyrequest().authenticated():需要对所有其他请求进行身份验证。

oauth2resourceserver:将应用程序配置为使用 jwt 进行身份验证的 oauth 2.0 资源服务器。

config.jwt(jwt -> jwt.decoder(jwtdecoder())):指定将用于解码和验证 jwt 令牌的 jwt 解码器 bean (jwtdecoder)。

bcryptpasswordencoder:这个bean定义了一个密码编码器,它使用bcrypt哈希算法对密码进行编码。 bcrypt 因其自适应特性而成为安全存储密码的热门选择,使其能够抵抗暴力攻击。

jwtencoder:这个bean负责编码(签名)jwt令牌。

rsakey.builder:使用提供的公钥和私钥 rsa 密钥创建新的 rsa 密钥。immutablejwkset(new jwkset(jwk)):将 rsa 密钥包装在 json web 密钥集 (jwkset) 中,使其不可变。nimbusjwtencoder(jwks):使用 nimbus 库创建 jwt 编码器,该编码器将使用 rsa 私钥对令牌进行签名。

jwtdecoder:这个bean负责解码(验证)jwt令牌。

nimbusjwtdecoder.withpublickey(publickey).build():使用rsa公钥创建jwt解码器,用于验证jwt令牌的签名。 实体

import org.springframework.security.crypto.password.passwordencoder;import jakarta.persistence.column;import jakarta.persistence.entity;import jakarta.persistence.enumtype;import jakarta.persistence.enumerated;import jakarta.persistence.generatedvalue;import jakarta.persistence.generationtype;import jakarta.persistence.id;import jakarta.persistence.table;import lombok.getter;import lombok.noargsconstructor;import lombok.setter;@entity@table(name = "tb_clients")@getter@setter@noargsconstructorpublic class cliententity {    @id    @generatedvalue(strategy = generationtype.sequence)    @column(name = "client_id")    private long clientid;    private string name;    @column(unique = true)    private string cpf;    @column(unique = true)    private string email;    private string password;    @column(name = "user_type")    private string usertype = "client";    public boolean islogincorrect(string password, passwordencoder passwordencoder) {        return passwordencoder.matches(password, this.password);    }}

存储库

import java.util.optional;import org.springframework.data.jpa.repository.jparepository;import org.springframework.stereotype.repository;import example.com.challengepicpay.entities.cliententity;@repositorypublic interface clientrepository extends jparepository {    optional findbyemail(string email);    optional findbycpf(string cpf);    optional findbyemailorcpf(string email, string cpf);}

服务

客户服务

import org.springframework.beans.factory.annotation.autowired;import org.springframework.http.httpstatus;import org.springframework.security.crypto.bcrypt.bcryptpasswordencoder;import org.springframework.stereotype.service;import org.springframework.web.server.responsestatusexception;import example.com.challengepicpay.entities.cliententity;import example.com.challengepicpay.repositories.clientrepository;@servicepublic class clientservice {    @autowired    private clientrepository clientrepository;    @autowired    private bcryptpasswordencoder bpasswordencoder;    public cliententity createclient(string name, string cpf, string email, string password) {        var clientexists = this.clientrepository.findbyemailorcpf(email, cpf);        if (clientexists.ispresent()) {            throw new responsestatusexception(httpstatus.bad_request, "email/cpf already exists.");        }        var newclient = new cliententity();        newclient.setname(name);        newclient.setcpf(cpf);        newclient.setemail(email);        newclient.setpassword(bpasswordencoder.encode(password));        return clientrepository.save(newclient);    }}

代币服务

import java.time.instant;import org.springframework.beans.factory.annotation.autowired;import org.springframework.http.httpstatus;import org.springframework.security.authentication.badcredentialsexception;import org.springframework.security.crypto.bcrypt.bcryptpasswordencoder;import org.springframework.security.oauth2.jwt.jwtclaimsset;import org.springframework.security.oauth2.jwt.jwtencoder;import org.springframework.security.oauth2.jwt.jwtencoderparameters;import org.springframework.stereotype.service;import org.springframework.web.server.responsestatusexception;import example.com.challengepicpay.repositories.clientrepository;@servicepublic class tokenservice {    @autowired    private clientrepository clientrepository;    @autowired    private jwtencoder jwtencoder;    @autowired    private bcryptpasswordencoder bcryptpasswordencoder;    public string login(string email, string password) {        var client = this.clientrepository.findbyemail(email)                .orelsethrow(() -> new responsestatusexception(httpstatus.bad_request, "email not found"));        var iscorrect = client.islogincorrect(password, bcryptpasswordencoder);        if (!iscorrect) {            throw new badcredentialsexception("email/password invalid");        }        var now = instant.now();        var expiresin = 300l;        var claims = jwtclaimsset.builder()                .issuer("pic_pay_backend")                .subject(client.getemail())                .issuedat(now)                .expiresat(now.plusseconds(expiresin))                .claim("scope", client.getusertype())                .build();        var jwtvalue = jwtencoder.encode(jwtencoderparameters.from(claims)).gettokenvalue();        return jwtvalue;    }}

控制器

客户端控制器

package example.com.challengepicpay.controllers;import org.springframework.beans.factory.annotation.autowired;import org.springframework.http.httpstatus;import org.springframework.http.responseentity;import org.springframework.web.bind.annotation.postmapping;import org.springframework.web.bind.annotation.requestbody;import org.springframework.web.bind.annotation.restcontroller;import org.springframework.security.oauth2.server.resource.authentication.jwtauthenticationtoken;import example.com.challengepicpay.controllers.dto.newclientdto;import example.com.challengepicpay.entities.cliententity;import example.com.challengepicpay.services.clientservice;@restcontrollerpublic class clientcontroller {    @autowired    private clientservice clientservice;    @postmapping("/signin")    public responseentity createnewclient(@requestbody newclientdto client) {        var newclient = this.clientservice.createclient(client.name(), client.cpf(), client.email(), client.password());        return responseentity.status(httpstatus.created).body(newclient);    }    @getmapping("/protectedroute")    @preauthorize("hasauthority('scope_client')")    public responseentity protectedroute(jwtauthenticationtoken token) {        return responseentity.ok("authorized");    }}

解释

/protectedroute 是私有路由,登录后只能使用 jwt 访问。

例如,令牌必须作为不记名令牌包含在标头中。

您可以稍后在应用程序中使用令牌信息,例如在服务层中。

@preauthorize:spring security中的@preauthorize注解用于在调用方法之前执行授权检查。此注释通常应用于 spring 组件(如控制器或服务)中的方法级别,以根据用户的角色、权限或其他安全相关条件来限制访问。注解用于定义方法执行必须满足的条件。如果条件评估为真,则该方法继续进行。如果评估结果为 false,则访问被拒绝,

“hasauthority(‘scope_client’)”:检查当前经过身份验证的用户或客户端是否具有特定权限 scope_client。如果这样做,则执行 protectedroute() 方法。如果不这样做,访问将被拒绝。

token controller:在这里,你可以登录应用程序,如果成功,会返回一个token。

package example.com.challengePicPay.controllers;import java.util.Map;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.RestController;import example.com.challengePicPay.controllers.dto.LoginDTO;import example.com.challengePicPay.services.TokenService;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;@RestControllerpublic class TokenController {    @Autowired    private TokenService tokenService;    @PostMapping("/login")    public ResponseEntity<Map> login(@RequestBody LoginDTO loginDTO) {        var token = this.tokenService.login(loginDTO.email(), loginDTO.password());        return ResponseEntity.ok(Map.of("token", token));    }}

参考

春季安全spring security-toptal 文章

以上就是Spring Security 与 JWT的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/494100.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
宫崎英高承认其非PVP爱好者:《The Duskbloods》依旧有PVE要素
上一篇 2025年11月8日 17:07:14
6自由度机器人的下一个进化阶段
下一篇 2025年11月8日 17:07:23

相关推荐

发表回复

登录后才能评论
关注微信