鉴权.1

简介: 本文介绍基于JWT与Spring Security的鉴权实现方案,涵盖Token认证流程、pom依赖配置、安全类WebSecurityConfig设置、JWT生成与验证、自定义认证组件等内容,结合代码示例完成前后端分离场景下的安全控制。

6.鉴权

1.客户端Token方案
1.1 实现思路



1.2 实现细节
参考:https://www.cnblogs.com/dalaoyang/p/11783225.html
2.JWT + Security
RFC7519
JWT

JWT很大程度上还是个新技术,通过使用HMAC(Hash-based Message Authentication Code)计算信息摘要,也可以用RSA公私钥中的私钥进行签名。这个根据业务场景进行选择。
2.1 pom依赖

<!--JWT依赖开始-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt</artifactId>
  <version>0.7.0</version>
</dependency>
<!--JWT依赖结束-->

/login进行登录并获得Token。剩余接口做token验签,这里我们需要将spring-boot-starter-security加入pom.xml。加入后,我们的Spring Boot项目将需要提供身份验证,相关的pom.xml如下:


至此我们剩余所有的路由都需要身份验证。我们将引入一个安全设置类WebSecurityConfig,这个类需要从WebSecurityConfigurerAdapter类继承。

2.2 安全设置类WebSecurityConfig

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    // 设置 HTTP 验证规则
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 关闭csrf验证
        http.csrf().disable()
                // 对请求进行认证
                .authorizeRequests()
                // 所有 / 的所有请求 都放行
                .antMatchers("/").permitAll()
                // 所有 /login 的POST请求 都放行
                .antMatchers(HttpMethod.POST, "/login").permitAll()
                // 权限检查
                .antMatchers("/hello").hasAuthority("AUTH_WRITE")
                // 角色检查
                .antMatchers("/world").hasRole("ADMIN")
                // 所有请求需要身份认证
                .anyRequest().authenticated()
            .and()
                // 添加一个过滤器 所有访问 /login 的请求交给 JWTLoginFilter 来处理 这个类处理所有的JWT相关内容
                .addFilterBefore(new JWTLoginFilter("/login", authenticationManager()),
                        UsernamePasswordAuthenticationFilter.class)
                // 添加一个过滤器验证其他请求的Token是否合法
                .addFilterBefore(new JWTAuthenticationFilter(),
                        UsernamePasswordAuthenticationFilter.class);
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 使用自定义身份验证组件
        auth.authenticationProvider(new CustomAuthenticationProvider());
    }
    
    // 注入自定义Bean,保证该类能够注入其它Bean,如果没有这步将导致CustomAuthenticationProvider中注入Bean失败
    @Bean
    CustomAuthenticationProvider customAuthenticationProvider() {
        return new CustomAuthenticationProvider();
    }
}

先放两个基本类,一个负责存储用户名密码,另一个是一个权限类型,负责存储权限和角色。

2.3 权限类型及角色类

import org.springframework.security.core.GrantedAuthority;

class GrantedAuthorityImpl implements GrantedAuthority{
    private String authority;
    public GrantedAuthorityImpl(String authority) {
        this.authority = authority;
    }
    public void setAuthority(String authority) {
        this.authority = authority;
    }
    @Override
    public String getAuthority() {
        return this.authority;
    }
}

2.4 用户名密码类

class AccountCredentials {
    private String username;
    private String password;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

在上面的安全设置类中,我们设置所有人都能访问/POST方式访问/login,其他的任何路由都需要进行认证。然后将所有访问/login的请求,都交给JWTLoginFilter过滤器来处理。稍后我们会创建这个过滤器和其他这里需要的JWTAuthenticationFilterCustomAuthenticationProvider两个类。

2.5 JWT生成及验签类

import com.test.framework.client.dto.response.JSONResultDTO;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;

import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.List;

class TokenAuthenticationService {
    // 5天(单位ms,需要是24H的整数倍:如0.1倍,1倍,10倍,不能0.34倍)
    static final long EXPIRATIONTIME = 432_000_000;     
    static final String SECRET = "P@ssw02d";            // JWT密码
    static final String TOKEN_PREFIX = "Bearer";        // Token前缀
    static final String HEADER_STRING = "Authorization";// 存放Token的Header Key
  // JWT生成方法
    static void addAuthentication(HttpServletResponse response, String username) {
    // 生成JWT
        String JWT = Jwts.builder()
                // 保存权限(角色)
                .claim("authorities", "ROLE_ADMIN,AUTH_WRITE")
                // 用户名写入标题
                .setSubject(username)
                // 有效期设置
                        .setExpiration(new Date(System.currentTimeMillis() + EXPIRATIONTIME))
                // 签名设置
                        .signWith(SignatureAlgorithm.HS512, SECRET)
                        .compact();
        // 将 JWT 写入 body
        try {
            response.setContentType("application/json");
            response.setStatus(HttpServletResponse.SC_OK);
            response.getOutputStream().println(JSONResult.fillResultString(0, "", JWT));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  // JWT验证方法
    static Authentication getAuthentication(HttpServletRequest request) {
        // 从Header中拿到token
        String token = request.getHeader(HEADER_STRING);
        if (token != null) {
            // 解析 Token
            Claims claims = Jwts.parser()
                    // 验签
                    .setSigningKey(SECRET)
                    // 去掉 Bearer
                    .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
                    .getBody();
            // 拿用户名
            String user = claims.getSubject();
            // 得到 权限(角色)
            List<GrantedAuthority> authorities =  AuthorityUtils.commaSeparatedStringToAuthorityList((String) claims.get("authorities"));
            // 返回验证令牌
            return user != null ?
                    new UsernamePasswordAuthenticationToken(user, null, authorities) :
                    null;
        }
        return null;
    }
}

这个类就两个static方法,一个负责生成JWT,一个负责认证JWT最后生成验证令牌。注释已经写得很清楚了,这里不多说了。

下面来看自定义验证组件,这里简单写了,这个类就是提供密码验证功能,在实际使用时换成自己相应的验证逻辑,从数据库中取出、比对、赋予用户相应权限。

2.6 自定义验证组件类

import com.test.framework.web.domain.dbdo.doctor.DoctorDTO;
import com.test.framework.web.domain.dbdo.patient.UserDTO;
import com.test.framework.web.domain.vo.GrantedAuthorityVo;
import com.test.framework.web.service.doctor.DoctorService;
import com.test.framework.web.service.user.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;

import java.util.ArrayList;


// 自定义身份认证验证组件
class CustomAuthenticationProvider implements AuthenticationProvider {
    
    @Autowired
    private UserService userService;
    
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 获取认证的用户名 & 密码
        String name = authentication.getName();
        String password = authentication.getCredentials().toString();
        // 认证逻辑,我这里以password为类型,name为真正的查询参数进行DB查询,不同业务场景可以自定义参数查询
        // 验证用户名密码是否存在
        boolean isExist = false;
        if("patient".equalsIgnoreCase(password)) {
            // 查询患者信息是否存在
            UserDTO user = userService.getUserByIdCardNo(name);
            if(null != user) {
                isExist = true;
            }
        }
        if (isExist) {
            // 这里设置权限和角色
            ArrayList<GrantedAuthority> authorities = new ArrayList<>();
            authorities.add( new GrantedAuthorityImpl("ROLE_ADMIN") );
            authorities.add( new GrantedAuthorityImpl("AUTH_WRITE") );
            // 生成令牌
            Authentication auth = new UsernamePasswordAuthenticationToken(name, password, authorities);
            return auth;
        }else {
            throw new BadCredentialsException("密码错误~");
        }
    }
    // 是否可以提供输入类型的认证服务
    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}


目录
相关文章
|
算法 测试技术 编译器
掌握CTest:CTest综合指南
掌握CTest:CTest综合指南
1283 1
|
5月前
|
负载均衡 应用服务中间件 Nacos
Nacos配置中心
本文详细介绍Nacos作为配置中心的实现原理与实战步骤,涵盖配置管理、热更新、共享配置优先级及集群搭建,帮助微服务应用实现配置动态化、高可用部署。
284 4
|
5月前
|
Java Spring
什么是WebFlux
Spring WebFlux 是 Spring Framework 5 引入的响应式Web框架,支持非阻塞、事件驱动的编程模型,适用于高并发场景,可运行于 Netty、Undertow 等服务器,提供注解式和函数式编程接口。
103 2
|
5月前
|
JSON Dubbo Java
Feign远程调用
本章介绍如何使用Feign替代RestTemplate实现更优雅的HTTP跨服务调用。通过引入Feign,结合注册中心与注解声明,解决硬编码、可读性差等问题,并支持日志、连接池等自定义配置。同时提出继承与抽取两种最佳实践,推荐将Feign客户端抽离为独立模块,提升代码复用性与维护性,助力微服务架构优化。
186 0
Feign远程调用
|
5月前
|
存储 缓存 负载均衡
Nacos注册中心
本文介绍Nacos的安装部署、服务注册与发现、分级模型、负载均衡策略、权重控制、环境隔离及实例类型等内容,涵盖从入门到实战的核心知识点,帮助开发者快速掌握Nacos在微服务架构中的应用。
176 0
 Nacos注册中心
|
27天前
|
存储 新零售 运维
2026年阿里云盘企业版收费标准:6元1个月、169元1年,企业网盘功能、优势及使用场景说明
2026年阿里云盘企业版(CDE)重磅升级:https://t.aliyun.com/U/wbANEa 配置5人起步,200GB仅6.63元/月,500GB年付169.90元!具备多人在线编辑、精细权限管控、全文检索、秒传加速及金融级安全等五大核心能力,适配新零售、制造、教育、医药等多场景,性价比远超NAS与消费级网盘。
485 5
|
5月前
|
负载均衡 Java 数据安全/隐私保护
Gateway服务网关
网关是微服务架构的统一入口,核心功能包括请求路由、权限控制和限流。通过Spring Cloud Gateway可实现高效路由转发与过滤器处理,支持全局过滤与跨域解决方案,提升系统安全性和稳定性。(239字)
190 0
|
4月前
|
数据采集 运维 搜索推荐
京东商品详情API接口:电商数据驱动的核心入口解析
京东商品详情API(如jd.union.open.goods.detail.query)是官方合规、稳定、实时的商品数据接口,支持获取全维度商品信息(价格、库存、促销、评价等),广泛应用于比价导购、竞品分析、智能推荐与自动化运营,助力企业高效、合法地释放电商数据价值。(239字)
|
7月前
|
人工智能 数据挖掘 API
单独学智能体,是永远无法变现的
智能体本身不赚钱,落地场景才创造价值。从教育、营销到生活服务,唯有将AI嵌入具体痛点,构建可复制的工作流并包装为产品,才能实现商业化。学技术是起点,解决问题才是终点。真正会赚钱的,是让智能体在真实场景中持续创造价值的人。
|
6月前
|
数据采集 存储 NoSQL
用Redis实现爬虫URL去重与队列管理:从原理到实战的极简指南
本文详解Redis在爬虫中的核心应用:利用SET与BloomFilter实现高效URL去重,结合LIST、BRPOP与ZSET构建高性能任务队列,并支持分布式协作。通过代码示例与实战优化技巧,助你打造亿级规模、高并发的智能爬虫系统,显著提升抓取效率与稳定性。
385 0

热门文章

最新文章