兰州网站建设优化制作公司做网站为什么选择竞网智赢
- 作者: 五速梦信息网
- 时间: 2026年04月20日 10:33
当前位置: 首页 > news >正文
兰州网站建设优化制作公司,做网站为什么选择竞网智赢,wordpress 统计小工具,青岛建设工程信息网前言 在结束理论知识的学习后#xff0c;荔枝开始项目学习#xff0c;这个系列文章将围绕荔枝学习mall项目过程中总结的知识点来梳理。本篇文章主要涉及如何整合Spring Security和JWT实现鉴权认证的功能#xff01;希望能帮助到一起学习mall项目的小伙伴~~~ 文章目录 前言 …前言 在结束理论知识的学习后荔枝开始项目学习这个系列文章将围绕荔枝学习mall项目过程中总结的知识点来梳理。本篇文章主要涉及如何整合Spring Security和JWT实现鉴权认证的功能希望能帮助到一起学习mall项目的小伙伴~~~ 文章目录 前言 一、JWT和Spring Security的整合知识点 1.1 JWTtoken的生成流程 1.1.1 什么是CSRF 1.1.2 为什么需要JWT 1.1.3 JWTtoken的生成流程 1.2 UserDetails接口 1.3 CollUtil类 1.4 PasswordEncoder接口 1.5 为什么要实现Serializable接口 总结 一、JWT和Spring Security的整合知识点 这部分会根据mall_learning中的后台用户管理实现类来做系统的梳理学习目的主要是弄清楚JWT token的生成流程以及相应的类的使用。首先我们来看一下该实现类的demo: package com.crj.crj_mall_learning.service.impl;import cn.hutool.core.collection.CollUtil; import com.crj.crj_mall_learning.utils.JwtTokenUtil; import com.crj.crj_mall_learning.domain.AdminUserDetails; import com.crj.crj_mall_learning.domain.UmsResource; import com.crj.crj_mall_learning.service.UmsAdminService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service;import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors;/*** auther lzddl* description 后台用户管理Service实现类/ Slf4j Service public class UmsAdminServiceImpl implements UmsAdminService {/** 存放默认用户信息/private ListAdminUserDetails adminUserDetailsList new ArrayList();/** 存放默认资源信息/private ListUmsResource resourceList new ArrayList();Autowiredprivate JwtTokenUtil jwtTokenUtil;//spring security中的一种对密码的编译方法Autowiredprivate PasswordEncoder passwordEncoder;// Spring IOC容器的初始化中就会执行该init方法PostConstructprivate void init(){adminUserDetailsList.add(AdminUserDetails.builder().username(admin).password(passwordEncoder.encode(123456)).authorityList(CollUtil.toList(brand:create,brand:update,brand:delete,brand:list,brand:listAll)).build());adminUserDetailsList.add(AdminUserDetails.builder().username(lzddl).password(passwordEncoder.encode(123456)).authorityList(CollUtil.toList(brand:listAll)).build());resourceList.add(UmsResource.builder().id(1L).name(brand:create).url(/brand/create).build());resourceList.add(UmsResource.builder().id(2L).name(brand:update).url(/brand/update/).build());resourceList.add(UmsResource.builder().id(3L).name(brand:delete).url(/brand/delete/).build());resourceList.add(UmsResource.builder().id(4L).name(brand:list).url(/brand/list).build());resourceList.add(UmsResource.builder().id(5L).name(brand:listAll).url(/brand/listAll).build());}Overridepublic AdminUserDetails getAdminByUsername(String username) {//在存放默认用户对象的集合中来查找指定的用户信息如果有就返回符合条件的第一条数据// 如果没有就会返回一个nullListAdminUserDetails findList adminUserDetailsList.stream().filter(item - item.getUsername().equals(username)).collect(Collectors.toList());if(CollUtil.isNotEmpty(findList)){return findList.get(0);}return null;}Overridepublic ListUmsResource getResourceList() {return resourceList;}Overridepublic String login(String username, String password) {String token null;try {//这里的UserDetails是Spring Security中的一个接口该接口实现仅仅存储用户的信息后续会将该接口提供的用户信息封装到认证对象Authentication中去UserDetails userDetails getAdminByUsername(username);if(userDetailsnull){return token;}if (!passwordEncoder.matches(password, userDetails.getPassword())) {throw new BadCredentialsException(密码不正确);}UsernamePasswordAuthenticationToken authentication new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());SecurityContextHolder.getContext().setAuthentication(authentication);token jwtTokenUtil.generateToken(userDetails);} catch (AuthenticationException e) {log.warn(登录异常:{}, e.getMessage());}return token;} }1.1 JWTtoken的生成流程 在正式了解整个认证鉴权的流程之前我们首先需要弄清楚为什么需要使用JWT在以往的前后端半分离的项目中我们经常使用会话来实现token值的缓存由于前后端没有涉及过多的跨域操作因此也就不会采用JWT来保证跨域请求的安全。那为什么跨域请求中需要采用JWT来认证授权呢 1.1.1 什么是CSRF 跨站请求伪造Cross-site request forgery也被称为 one-click attack 或者session riding通常缩写为 CSRF 或者 XSRF是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。 1.1.2 为什么需要JWT JWT的token值主要是由三部分组成的header.payload.signature。该token是用户访问正规网站会拿到的并缓存在用户浏览器的localStorage本地缓存里面。header里面是加密算法的信息里面放typejwtalg(加密算法)HS512payload里面存放的是用户名、token的创建时间和过期时间signature通过使用head中定义的加密算法和后端已经设置好的加密密钥将headpayload这两部分加密生成一个signature签名最终拼接所有密钥数据加起来生成一个JWT令牌也就是token。由于JWT的token存在用户浏览器的localStorage本地缓存里面下次这个用户发送请求给网站的后台时就会把这个JWT发给正规网站的后台进行安全性校验而不会向之前那种模式那样使用到cookie。这就可以避免用户在浏览网站的时候被恶意链接获取到cookies从而绕开网站的安全性校验而导致用户的信息被窃取或造成其它损失。 1.1.3 JWTtoken的生成流程 首先用户通过后端提供的校验授权的接口来执行登录的操作这部分的功能主要有两个校验和授权。首先当用户第一次登录的时候会根据账号和密码来获得JWT生成的token在上面的介绍中我们已经知晓该token值的功能。 首次登录 首先用户根据账号和密码进行校验在Spring Security中我们可以选择在初始化接口实现类的时候就将符合条件的用户信息加载在一个UserDetails对象构成的列表中并根据默认要求对密码采用passwordEncoder类方法进行encode。因此我们在校验密码的正误的时候也需要采用该类方法对用户输入的密码进行比较。完成身份校验后才会开始生成token首先需要将用户信息对象UserDetails和用户的访问权限列表交给UsernamePasswordAuthenticationToken对象的构造方法构造一个UsernamePasswordAuthenticationToken对象之后会交给一个SecurityContextHolder来管理该用户的信息最后才会调用你自定义的token工具类生成JWT token值。 //用户校验完成获取一个UsernamePasswordAuthenticationToken对象 UsernamePasswordAuthenticationToken authentication new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); //将UsernamePasswordAuthenticationToken对象交给SecurityContextHolder表示用户已经通过身份验证这一步是为了让SpringSecurity知晓用户对象是谁及其权限并判断该用户是否有相应的访问资源的权限 SecurityContextHolder.getContext().setAuthentication(authentication); //获取token token jwtTokenUtil.generateToken(userDetails); token生成的具体流程 在下面的demo中使用了一个map对象来设置主题信息并在重载方法中通过setClaims方法来实现主题信息的声明后续可以根据UserDetails对象的getSubject()方法来获得用户名。这个UserDetails对象的获取其实就是根据用户输入的用户名在已经缓存的用户信息List中找到对应的用户信息对象。 /** 根据负载生成JWT的token/private String generateToken(MapString, Object claims) {return Jwts.builder().setClaims(claims) // 设置JWT的声明claims即要在JWT中存储的信息。这个参数是一个MapString, Object表示JWT的payload部分。.setExpiration(generateExpirationDate()) //过期时间.signWith(SignatureAlgorithm.HS512, secret) //根据后台设置的密钥来生成签名.compact(); //构建并返回最终的JWT字符串} /** 根据用户信息生成token/public String generateToken(UserDetails userDetails) {MapString, Object claims new HashMap();claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());claims.put(CLAIM_KEY_CREATED, new Date());return generateToken(claims);} 拿到token后用户每次调用接口都在http的headert中添加一个叫Authorization的头值为WT的token后台程序通过对Authorization头中信息的解码及数字签名校验来获取其中的用户信息从而实现认证和授权。 授权 在上文中我们已经弄清楚了整个JWT的token令牌生成的流程了接下来我们需要搞清楚如何整合Spring Security完成用户权限的授权。首先同样的我们事先需要获取到所有用户的权限以及信息这部分内容并不是现在我们的重点。前面我们知道我们需要把用户信息存在一个个UserDetails对象当用户成功登录时UserDetails对象会被封装成Authentication对象并且设置到Spring Security的安全上下文中通常是SecurityContextHolder以便在后续的请求中进行鉴权和授权。首先我们需要弄清楚Spring Security的配置类 /** auther lzddl* description SpringSecurity的配置/ Configuration EnableWebSecurity EnableGlobalMethodSecurity(prePostEnabledtrue) public class SecurityConfig {Autowiredprivate RestfulAccessDeniedHandler restfulAccessDeniedHandler;Autowiredprivate RestAuthenticationEntryPoint restAuthenticationEntryPoint;Autowiredprivate IgnoreUrlsConfig ignoreUrlsConfig;BeanSecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {ExpressionUrlAuthorizationConfigurerHttpSecurity.ExpressionInterceptUrlRegistry registry httpSecurity.authorizeRequests();//不需要保护的资源路径允许访问for (String url : ignoreUrlsConfig.getUrls()) {registry.antMatchers(url).permitAll();}//允许跨域请求的OPTIONS请求这是因为在跨域访问的正式请求发送前会发送一个Option请求我们要开启跨域访问就必须允许所有的option请求registry.antMatchers(HttpMethod.OPTIONS).permitAll();httpSecurity.csrf()// 由于使用的是JWT我们这里不需要csrf.disable().sessionManagement()// 基于token所以不需要session下面设置session为无状态的.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().anyRequest()// 除上面外的所有请求全部需要鉴权认证.authenticated();// 禁用缓存httpSecurity.headers().cacheControl();// 添加JWT filterhttpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);//添加自定义未授权和未登录结果返回httpSecurity.exceptionHandling().accessDeniedHandler(restfulAccessDeniedHandler).authenticationEntryPoint(restAuthenticationEntryPoint);return httpSecurity.build();}Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}Beanpublic JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){return new JwtAuthenticationTokenFilter();}}我们需要使用SecurityFilterChain来配置相关的操作首先我们需要获取到httpSecurity对象然后配置相应的访问路径白名单、允许跨域的Option请求、禁用csrf和session、开启除白名单外的鉴权认证功能、禁用缓存并添加JWT过滤器、添加自定义未授权和未登录结果返回。以上就是我们需要整合SpringSecurity的内容。其中鉴权部分我们需要注意的是JwtAuthenticationTokenFilter 部分因为我们在配置类中开启了将filter放置在登录验证的功能前因此这个功能其实就是为我们提供一个可以通过token记住用户态的功能。 /** auther lzddl* description JWT登录授权过滤器/ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {private static final Logger LOGGER LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);Autowiredprivate UserDetailsService userDetailsService;Autowiredprivate JwtTokenUtil jwtTokenUtil;Value(\({jwt.tokenHeader})private String tokenHeader;Value(\){jwt.tokenHead})private String tokenHead;Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain chain) throws ServletException, IOException {String authHeader request.getHeader(this.tokenHeader);if (authHeader ! null authHeader.startsWith(this.tokenHead)) { // 获取tokenString authToken authHeader.substring(this.tokenHead.length());// The part after Bearer // 根据token获取用户名String username jwtTokenUtil.getUserNameFromToken(authToken);LOGGER.info(checking username:{}, username);//若有则获取用户信息并创建authentication对象if (username ! null SecurityContextHolder.getContext().getAuthentication() null) {UserDetails userDetails this.userDetailsService.loadUserByUsername(username);if (jwtTokenUtil.validateToken(authToken, userDetails)) {UsernamePasswordAuthenticationToken authentication new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));LOGGER.info(authenticated user:{}, username);SecurityContextHolder.getContext().setAuthentication(authentication);}}}chain.doFilter(request, response);} }真正的授权其实在用户信息对象的加载过程中就完成了 当然了接口也需要使用PreAuthorize注解来定义接口需要的权限在配置类中我们还要EnableGlobalMethodSecurity(prePostEnabledtrue)注解来开启方法级的安全性 1.2 UserDetails接口 该接口和UsernamePasswordAuthenticationToken类其实都是Spring Security中core包下管理。该接口定义了一个用户信息管理的api要想使用该接口我们需要自定义实现一个实现类在Spring Security中按照我们自定义的来动态权限配置列表这里荔枝将其命名为AdminUserDetails类。 /** auther lzddl* description SpringSecurity用户信息封装类/ Data EqualsAndHashCode(callSuper false) Builder public class AdminUserDetails implements UserDetails {private String username;private String password;private ListString authorityList;Overridepublic Collection? extends GrantedAuthority getAuthorities() {return this.authorityList.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());}Overridepublic String getPassword() {return this.password;}Overridepublic String getUsername() {return this.username;}Overridepublic boolean isAccountNonExpired() {return true;}Overridepublic boolean isAccountNonLocked() {return true;}Overridepublic boolean isCredentialsNonExpired() {return true;}Overridepublic boolean isEnabled() {return true;} }this.authorityList.stream()是将authorityList列表对象转化成一个Stream方便我们操作集合对象map()定义了authorityList中的每个字符串对象到SimpleGrantedAuthority 对象的映射关系SimpleGrantedAuthority 是Spring Security提供的一个实现了GrantedAuthority 接口的简单权限授予类它表示用户的权限。最后就是借助collect将SimpleGrantedAuthority 对象收集在一个List对象中。也就是说这个List包含了用户具有的权限可以被Spring Security用于进行身份验证和授权。 1.3 CollUtil类 CollUtil类是Hutool插件中有关集合操作的工具类里面封装了大量的操作集合数据的方法。 中文文档https://www.hutool.cn/docs/#/ API手册https://apidoc.gitee.com/dromara/hutool/ 1.4 PasswordEncoder接口 该接口是一个密码解析器一般用来加密。Spring Security 要求容器中必须有 PasswordEncoder 实例因此我们在用户的信息中必须使用该解析器来对密码进行解析。 // // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) //package org.springframework.security.crypto.password;public interface PasswordEncoder {String encode(CharSequence rawPassword);boolean matches(CharSequence rawPassword, String encodedPassword);default boolean upgradeEncoding(String encodedPassword) {return false;} }当然了该接口有很多实现类我们可以创建该接口的实现类对象来指定加密的规则。但是如果我们使用的是AutoWired注解的方式来将接口进行依赖注入那么默认使用加密算法BCrypt。如果要修改加密算法我们可以在配置类声明bean对象的时候修改 /** auther lzddl* description SpringSecurity的配置*/ Configuration EnableWebSecurity EnableGlobalMethodSecurity(prePostEnabledtrue) public class SecurityConfig {Autowiredprivate RestfulAccessDeniedHandler restfulAccessDeniedHandler;Autowiredprivate RestAuthenticationEntryPoint restAuthenticationEntryPoint;Autowiredprivate IgnoreUrlsConfig ignoreUrlsConfig;BeanSecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {……}Beanpublic PasswordEncoder passwordEncoder() {//使用MD4加密算法return new Md4PasswordEncoder();}Beanpublic JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){return new JwtAuthenticationTokenFilter();}}1.5 为什么要实现Serializable接口 说起Serializable接口我们一定会提到的一个名词就是序列化那么什么是序列化呢 Serializable接口是一个语义级别的一个接口属于Java的io包中定义的接口该接口没有定义任何的方法只有实现了该接口的类才会有序列化和反序列化的状态。序列化和反序列化时Java在执行底层的IO读写操作也就是实现对象数据和文件字节流转化的时候告诉JVM的一种方式。实现了Serializable接口的类可以被ObjectOutputStream转换为字节流同时也可以通过ObjectInputStream再将其解析为对象。 可序列化是一个可以被继承的状态。同时为了保证不可序列化类的子类被序列化子类必须有一个无参的构造方法 serialVersionUID 序列化运行时与每个可序列化类关联一个版本号称为serialVersionUlD。在反序列化期间使用它来验证序列化对象的发送方和接收方已经为该对象加载了类与序列化兼容。类的一个类对象的serialVersionUlD与相应发送方的serialVersionUlD不同时会导致在反序列化时抛出异常InvalidclassException。所以我们在实现Serializable接口的时候一般还会要去尽量显示地定义serialVersionUID就比如 private static final long serialVersionUID 1L; 记住一定要是final类型的 总结 在正式开发项目中我个人认为可能鉴权这部分的功能会更加复杂一点因为脚手架只是一个快速让我们接触上手的内容因此这部分token拼接和请求头的生成其实是在Swagger中手动输入的哈哈哈。然后最重要的还是要弄清楚整个整合的流程需要自定义的配置以及相应的鉴权流程当然了JWTtoken的结构也是比较重要的 今朝已然成为过去明日依然向往未来我是荔枝在技术成长之路上与您相伴~~~ 如果博文对您有帮助的话可以给荔枝一键三连嘿您的支持和鼓励是荔枝最大的动力 如果博文内容有误也欢迎各位大佬在下方评论区批评指正
相关文章
-
兰州网站建设推荐q479185700顶上哪些网站可以免费发帖做推广
兰州网站建设推荐q479185700顶上哪些网站可以免费发帖做推广
- 技术栈
- 2026年04月20日
-
兰州网站建设价深圳宝安区深圳网站建设 骏域网络
兰州网站建设价深圳宝安区深圳网站建设 骏域网络
- 技术栈
- 2026年04月20日
-
兰州网站建设方案详细做营销网站企业
兰州网站建设方案详细做营销网站企业
- 技术栈
- 2026年04月20日
-
兰州网站建设招聘最新中国燃气企业门户
兰州网站建设招聘最新中国燃气企业门户
- 技术栈
- 2026年04月20日
-
兰州网站建设专家wordpress 基本插件
兰州网站建设专家wordpress 基本插件
- 技术栈
- 2026年04月20日
-
兰州网站哪里做电脑平面设计软件
兰州网站哪里做电脑平面设计软件
- 技术栈
- 2026年04月20日
