0


Spring Boot 3 + JWT + Security 联手打造安全帝国:一篇文章让你掌握未来!

文章目录

前言

Spring Security

已经成为

java

后台权限校验的第一选择.今天就通过读代码的方式带大家深入了解一下Security,本文主要是基于开源项目spring-boot-3-jwt-security来讲解Spring Security + JWT(Json Web Token).实现用户鉴权,以及权限校验.
所有代码基于

jdk17+

构建.现在让我们开始吧!

1 技术简介

  1. Springboot 3.0
  2. Spring Security
  3. Json Web Token(JWT)
  4. BCrypt
  5. Maven

2 项目构建

  1. 项目使用postgresql数据库来存储用户信息以及Token(为啥不用Redis?这个先挖个坑),可以按照自己的想法替换成mysql数据库
  2. 访问数据库使用的是jpa,对于一些简单的sql可以根据方法名自动映射,还是很方便的.没使用过的也没关系.不影响阅读今天的文章,后续可以根据自己的实际需求替换成mybatis-lpus
  3. 本文使用了Lombok来生成固定的模版代码
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.0.5</version><relativePath/><!-- lookup parent from repository --></parent><groupId>com.alibou</groupId><artifactId>security</artifactId><version>0.0.1-SNAPSHOT</version><name>security</name><description>Demo project for Spring Boot</description><properties><java.version>17</java.version></properties><dependencies><!-- jpa --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><!-- spring security 安全框架 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!-- web 依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 数据库 --><dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId><scope>runtime</scope></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- JWT --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.11.5</version></dependency><!-- doc 这个不需要的可以去掉 --><dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId><version>2.1.0</version></dependency><!-- 校验 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build>

3 项目配置

3.1 鉴权配置

  1. 当项目引入Security依赖后,启动项目会生成一个随机的密码,当我们要访问资源的时候需要使用这个密码登录后才能使用.这会影响我们很多功能的正常使用,比如万恶的swagger.下面我们来详细了解如何配置我们需要鉴权的路径,以及需要放行的路径
@Configuration@EnableWebSecurity@RequiredArgsConstructor@EnableMethodSecuritypublicclassSecurityConfiguration{privatefinalJwtAuthenticationFilter jwtAuthFilter;privatefinalAuthenticationProvider authenticationProvider;privatefinalLogoutHandler logoutHandler;@BeanpublicSecurityFilterChainsecurityFilterChain(HttpSecurity http)throwsException{  
    http  
    .csrf().disable()//关闭csrf(跨域)  .authorizeHttpRequests()//配置需要放行的路径  .requestMatchers("/api/v1/auth/**","/v2/api-docs","/v3/api-docs","/v3/api-docs/**","/swagger-resources","/swagger-resources/**","/configuration/ui","/configuration/security","/swagger-ui/**","/webjars/**","/swagger-ui.html").permitAll()//放行上述的所有路径  /*  
    * 权限校验(需要登录的用户有指定的权限才可以)  
    * requestMatchers: 指定需要拦截的路径  
    * hasAnyAuthority: 指定需要的权限  
    */.requestMatchers("/api/v1/management/**").hasAnyRole(ADMIN.name(),MANAGER.name()).requestMatchers(GET,"/api/v1/management/**").hasAnyAuthority(ADMIN_READ.name(),MANAGER_READ.name()).requestMatchers(POST,"/api/v1/management/**").hasAnyAuthority(ADMIN_CREATE.name(),MANAGER_CREATE.name()).requestMatchers(PUT,"/api/v1/management/**").hasAnyAuthority(ADMIN_UPDATE.name(),MANAGER_UPDATE.name()).requestMatchers(DELETE,"/api/v1/management/**").hasAnyAuthority(ADMIN_DELETE.name(),MANAGER_DELETE.name()).anyRequest().authenticated()//设置所有的请求都需要验证  .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)//使用无状态Session  .and().authenticationProvider(authenticationProvider)//添加jwt过滤器  .addFilterBefore(jwtAuthFilter,UsernamePasswordAuthenticationFilter.class)//设置logout(当调用这个接口的时候, 会调用logoutHandler的logout方法)  .logout().logoutUrl("/api/v1/auth/logout").addLogoutHandler(logoutHandler).logoutSuccessHandler((request, response,authentication)->SecurityContextHolder.clearContext());return http.build();}}
  1. 上述代码主要实现了四块功能分别是: - 放行不需要鉴权的路径(注册&登录,swagger)- 配置访问特定的接口用户需要的权限.(比如想要删除用户必须要有删除用户的权限)- 添加前置过滤器,用来从Token中判断用户是否合法和获取用户权限: jwtAuthFilter- 配置退出登录的Handler,以及监听的路径.当访问这个路径的时候会自动调用logoutHandler中的方法

3.2 登录配置

上面说到了权限和

token

校验,我们先来了解一下登录的逻辑是什么样的.在

security

中需要一个

UserDetails

类来定义用户账户的行为.这个是用户鉴权的关键.主要有账户,密码,权限,用户状态等等.在下面代码中有详细的注释

@Data@Builder@NoArgsConstructor@AllArgsConstructor@Entity@Table(name ="_user")publicclassUserimplementsUserDetails{@Id@GeneratedValueprivateInteger id;//主键ID  privateString firstname;//名字  privateString lastname;//姓氏  privateString email;//邮箱  privateString password;//密码  /**  
    * 角色枚举  
    */@Enumerated(EnumType.STRING)privateRole role;/**  
    * 用户关联的Token  
    * 这里面使用了jpa的一对多映射  
    */@OneToMany(mappedBy ="user")privateList<Token> tokens;/**  
    * 获取用户的权限  
    * 这里是根据角色枚举的权限来获取的(静态的而非从数据库动态读取)  
    * @return 用户权限列表  
    */@OverridepublicCollection<?extendsGrantedAuthority>getAuthorities(){return role.getAuthorities();}/**  
    * 获取用户密码  
    * 主要是用来指定你的password字段  
    * @return 用户密码  
    */@OverridepublicStringgetPassword(){return password;}/**  
    * 获取用户账号  
    * 这里使用email做为账号  
    * @return 用户账号  
    */@OverridepublicStringgetUsername(){return email;}/**  
    * 账号是否未过期,下面的这个几个方法都是用来指定账号的状态的,因为该项目是一个Demo,所以这里全部返回true  
    * @return true 未过期  
    */@OverridepublicbooleanisAccountNonExpired(){returntrue;}/**  
    * 账号是否未锁定  
    * @return true 未锁定  
    */@OverridepublicbooleanisAccountNonLocked(){returntrue;}/**  
    * 密码是否未过期  
    * @return true 未过期  
    */@OverridepublicbooleanisCredentialsNonExpired(){returntrue;}/**  
    * 账号是否激活  
    * @return true 已激活  
    */@OverridepublicbooleanisEnabled(){returntrue;}}

在了解用户实体之后,我们来看一下是怎么来进行登录配置的.如何使用

securty

来帮我们管理用户密码的校验.下面我们来看一下

security

的整体配置

@Configuration@RequiredArgsConstructorpublicclassApplicationConfig{/**  
    * 访问用户数据表  
    */privatefinalUserRepository repository;/**  
    * 获取用户详情Bean  
    * 根据email查询是否存在用户,如果不存在throw用户未找到异常  
    */@BeanpublicUserDetailsServiceuserDetailsService(){//调用repository的findByEmail方法,来获取用户信息,如果存在则返回,如果不存在则抛出异常  return username -> repository.findByEmail(username)//这里使用的Option的orElseThrow方法,如果存在则返回,如果不存在则抛出异常  .orElseThrow(()->newUsernameNotFoundException("User not found"));}/**  
    * 身份验证Bean  
    * 传入获取用户信息的bean & 密码加密器  
    * 可以回看一下SecurityConfiguration中 AuthenticationProvider的配置,使用的就是这里注入到容器中的Bean  
    * 这个bean 主要是用于用户登录时的身份验证,当我们登录的时候security会帮我们调用这个bean的authenticate方法  
    */@BeanpublicAuthenticationProviderauthenticationProvider(){DaoAuthenticationProvider authProvider =newDaoAuthenticationProvider();//设置获取用户信息的bean  
        authProvider.setUserDetailsService(userDetailsService());//设置密码加密器  
        authProvider.setPasswordEncoder(passwordEncoder());return authProvider;}/**  
    * 身份验证管理器  
    */@BeanpublicAuthenticationManagerauthenticationManager(AuthenticationConfiguration config)throwsException{return config.getAuthenticationManager();}/**  
    * 密码加密器  
    * 主要是用来指定数据库中存储密码的加密方式,保证密码非明文存储  
    * 当security需要进行密码校验时,会把请求传进来的密码进行加密,然后和数据库中的密码进行比对  
    */@BeanpublicPasswordEncoderpasswordEncoder(){returnnewBCryptPasswordEncoder();}}

上述代码主要做了两件事:

  • 指定我们如何从数据库中根据用户账号获取用户信息
  • 指定用户密码的加密器passwordEncoder

现在大家可能会存在一个疑问,

security

怎么知道

User

实体中那个字段是我的账户,那个字段是我的密码?
不知道大家是否记得

UserDetails

类,也就是我们的

User

类.其中有两个方法

getPassword

&

getUsername

.这两个方法返回的就是账号和密码.

User

类中的还有几个其他的方法,可以根据我们实际的业务需求来对账号进行

禁用

等操作.

3.3 Token如何生成

token

的生成主要是使用工具包来实现,在本项目中Token中主要存储

用户信息

&

用户权限

,下面我们先看一下

token

工具包的代码.主要包括为: 生成

token

,从

token

中获取信息,以及验证

token
@ServicepublicclassJwtService{/**  
    * 加密盐值  
    */@Value("${application.security.jwt.secret-key}")privateString secretKey;/**  
    * Token失效时间  
    */@Value("${application.security.jwt.expiration}")privatelong jwtExpiration;/**  
    * Token刷新时间  
    */@Value("${application.security.jwt.refresh-token.expiration}")privatelong refreshExpiration;/**  
    * 从Token中获取Username  
    * @param token Token  
    * @return String  
    */publicStringextractUsername(String token){returnextractClaim(token,Claims::getSubject);}/**  
    * 从Token中回去数据,根据传入不同的Function返回不同的数据  
    * eg: String extractUsername(String token)  
    */public<T>TextractClaim(String token,Function<Claims,T> claimsResolver){finalClaims claims =extractAllClaims(token);return claimsResolver.apply(claims);}/**  
    * 生成Token无额外信息  
    */publicStringgenerateToken(UserDetails userDetails){returngenerateToken(newHashMap<>(), userDetails);}/**  
    * 生成Token,有额外信息  
    * @param extraClaims 额外的数据  
    * @param userDetails 用户信息  
    * @return String  
    */publicStringgenerateToken(Map<String,Object> extraClaims,UserDetails userDetails  
    ){returnbuildToken(extraClaims, userDetails, jwtExpiration);}/**  
    * 生成刷新用的Token  
    * @param userDetails 用户信息  
    * @return String  
    */publicStringgenerateRefreshToken(UserDetails userDetails  
    ){returnbuildToken(newHashMap<>(), userDetails, refreshExpiration);}/**  
    * 构建Token方法  
    * @param extraClaims 额外信息  
    * @param userDetails //用户信息  
    * @param expiration //失效时间  
    * @return String  
    */privateStringbuildToken(Map<String,Object> extraClaims,UserDetails userDetails,long expiration  
        ){returnJwts.builder().setClaims(extraClaims)//body  .setSubject(userDetails.getUsername())//主题数据  .setIssuedAt(newDate(System.currentTimeMillis()))//设置发布时间  .setExpiration(newDate(System.currentTimeMillis()+ expiration))//设置过期时间  .signWith(getSignInKey(),SignatureAlgorithm.HS256)//设置摘要算法  .compact();}/**  
    * 验证Token是否有效  
    * @param token Token  
    * @param userDetails 用户信息  
    * @return boolean  
    */publicbooleanisTokenValid(String token,UserDetails userDetails){finalString username =extractUsername(token);return(username.equals(userDetails.getUsername()))&&!isTokenExpired(token);}/**  
    * 判断Token是否过去  
    */privatebooleanisTokenExpired(String token){returnextractExpiration(token).before(newDate());}/**  
    * 从Token中获取失效时间  
    */privateDateextractExpiration(String token){//通用方法,传入一个Function,返回一个T  returnextractClaim(token,Claims::getExpiration);}/**  
    * 从Token中获取所有数据  
    */privateClaimsextractAllClaims(String token){returnJwts.parserBuilder().setSigningKey(getSignInKey()).build().parseClaimsJws(token).getBody();}/**  
    * 获取签名Key  
    * Token 加密解密使用  
    */privateKeygetSignInKey(){byte[] keyBytes =Decoders.BASE64.decode(secretKey);returnKeys.hmacShaKeyFor(keyBytes);}}

3.4 注册和登录

token

的生成已经看过了,下面该进入最关键的环节了.

用户注册

&

用户登录
  1. 用户注册: 接收到用户传递过来的信息,在数据库中生成用户信息(密码会通过passwordEncoder进行加密).用户信息保存成功后,会根据用户信息创建一个鉴权token和一个refreshToken
  2. 用户登录: 获取到用户传递的账号密码后,会创建一个UsernamePasswordAuthenticationToken对象.然后通过authenticationManagerauthenticate方法进行校验,如果出现错误会根据错误的不同抛出不同的异常.在实际开发中可以通过捕获的异常类型不同来创建响应提示.
@RestController@RequestMapping("/api/v1/auth")@RequiredArgsConstructorpublicclassAuthenticationController{privatefinalAuthenticationService service;/**  
    * 注册方法  
    * @param request 请求体  
    * @return ResponseEntity  
    */@PostMapping("/register")publicResponseEntity<AuthenticationResponse>register(@RequestBodyRegisterRequest request  
    ){returnResponseEntity.ok(service.register(request));}/**  
    * 鉴权(登录方法)  
    * @param request 请求体  
    * @return ResponseEntity  
    */@PostMapping("/authenticate")publicResponseEntity<AuthenticationResponse>authenticate(@RequestBodyAuthenticationRequest request  
    ){returnResponseEntity.ok(service.authenticate(request));}/**  
    * 刷新token  
    * @param request 请求体  
    * @param response 响应体  
    * @throws IOException 异常  
    */@PostMapping("/refresh-token")publicvoidrefreshToken(HttpServletRequest request,HttpServletResponse response  
    )throwsIOException{  
        service.refreshToken(request, response);}}

可以看出来

controller

中的方法都是对

service

方法的调用,我们现在看一下

service

中的代码

@Service@RequiredArgsConstructorpublicclassAuthenticationService{privatefinalUserRepository repository;//访问user数据库  privatefinalTokenRepository tokenRepository;//访问token数据库  privatefinalPasswordEncoder passwordEncoder;//密码加密器  privatefinalJwtService jwtService;//JWT 相关方法  privatefinalAuthenticationManager authenticationManager;//Spring Security 认证管理器  /**  
    * 注册方法  
    * @param request 请求体  
    * @return AuthenticationResponse(自己封装的响应结构)  
    */publicAuthenticationResponseregister(RegisterRequest request){//构建用户信息  var user =User.builder().firstname(request.getFirstname()).lastname(request.getLastname()).email(request.getEmail()).password(passwordEncoder.encode(request.getPassword())).role(request.getRole()).build();//将用户信息保存到数据库  var savedUser = repository.save(user);//通过JWT方法生成Token  var jwtToken = jwtService.generateToken(user);//生成RefreshToken(刷新Token使用)  var refreshToken = jwtService.generateRefreshToken(user);//将Token保存到数据库  saveUserToken(savedUser, jwtToken);//返回响应体  returnAuthenticationResponse.builder().accessToken(jwtToken).refreshToken(refreshToken).build();}/**  
    * 鉴权(登录)方法  
    * @param request 请求体  
    * @return AuthenticationResponse(自己封装的响应结构)  
    */publicAuthenticationResponseauthenticate(AuthenticationRequest request){//通过Spring Security 认证管理器进行认证  //如果认证失败会抛出异常 eg:BadCredentialsException 密码错误 UsernameNotFoundException 用户不存在  
        authenticationManager.authenticate(newUsernamePasswordAuthenticationToken(  
        request.getEmail(),  
        request.getPassword()));//通过邮箱查询用户信息,当前项目email就是账号  var user = repository.findByEmail(request.getEmail()).orElseThrow();//通过JWT方法生成Token  var jwtToken = jwtService.generateToken(user);//生成RefreshToken(刷新Token使用)  var refreshToken = jwtService.generateRefreshToken(user);//将之前所有的Token变成失效状态  revokeAllUserTokens(user);//保存新的Token到数据库  saveUserToken(user, jwtToken);//封装响应体  returnAuthenticationResponse.builder().accessToken(jwtToken).refreshToken(refreshToken).build();}/**  
    * 保存用户Token方法  
    * 构建Token实体后保存到数据库  
    * @param user 用户信息  
    * @param jwtToken Token  
    */privatevoidsaveUserToken(User user,String jwtToken){var token =Token.builder().user(user).token(jwtToken).tokenType(TokenType.BEARER).expired(false).revoked(false).build();  
        tokenRepository.save(token);}/**  
    * 将用户所有Token变成失效状态  
    * @param user 用户信息  
    */privatevoidrevokeAllUserTokens(User user){//获取用户所有有效的token  var validUserTokens = tokenRepository.findAllValidTokenByUser(user.getId());if(validUserTokens.isEmpty()){return;}//如果存在还为失效的token,将token置为失效  
        validUserTokens.forEach(token ->{  
        token.setExpired(true);  
        token.setRevoked(true);});  
        tokenRepository.saveAll(validUserTokens);}/**  
    * 刷新token方法  
    * @param request 请求体  
    * @param response 响应体  
    * @throws IOException 抛出IO异常  
    */publicvoidrefreshToken(HttpServletRequest request,HttpServletResponse response  
    )throwsIOException{//从请求头中获取中获取鉴权信息 AUTHORIZATION  finalString authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);finalString refreshToken;finalString userEmail;//如果鉴权信息为空或者不是以Bearer 开头的,直接返回  if(authHeader ==null||!authHeader.startsWith("Bearer ")){return;}//从鉴权信息中获取RefreshToken  
        refreshToken = authHeader.substring(7);//从RefreshToken中获取用户信息  
        userEmail = jwtService.extractUsername(refreshToken);if(userEmail !=null){//根据用户信息查询用户,如果用户不存在抛出异常  var user =this.repository.findByEmail(userEmail).orElseThrow();//验证Token是否有效  if(jwtService.isTokenValid(refreshToken, user)){//生成新的Token  var accessToken = jwtService.generateToken(user);revokeAllUserTokens(user);saveUserToken(user, accessToken);//生成新的Token和RefreshToken并通过响应体返回  var authResponse =AuthenticationResponse.builder().accessToken(accessToken).refreshToken(refreshToken).build();newObjectMapper().writeValue(response.getOutputStream(), authResponse);}}}}

上述代码主要说明了,

注册

&

登录

后返回

token

的流程,当前项目中由于

token

&

refreshToken

有效期较长所以选择了将

token

保存到数据库(个人观点!!!).可以根据自己业务的实际需求来决定是否需要保存到

redis

3.5 请求过滤

请求过滤主要是在每次请求的时候动态解析

token

来获取

用户信息

以及

权限

,来保证请求资源的安全性.防止越权访问等.

@Component@RequiredArgsConstructorpublicclassJwtAuthenticationFilterextendsOncePerRequestFilter{privatefinalJwtService jwtService;privatefinalUserDetailsService userDetailsService;privatefinalTokenRepository tokenRepository;@OverrideprotectedvoiddoFilterInternal(@NonNullHttpServletRequest request,@NonNullHttpServletResponse response,@NonNullFilterChain filterChain  
    )throwsServletException,IOException{//判断请求是否为登录请求,如果是登录请求则不进行处理  if(request.getServletPath().contains("/api/v1/auth")){  
            filterChain.doFilter(request, response);return;}//从请求头中获取鉴权authHeader  finalString authHeader = request.getHeader("Authorization");finalString jwt;finalString userEmail;//如果不存在Token或者Token不已Bearer开头,则不进行处理  if(authHeader ==null||!authHeader.startsWith("Bearer ")){  
            filterChain.doFilter(request, response);return;}//从authHeader中截取出Token信息  
        jwt = authHeader.substring(7);//从Token中获取userEmail(账户)  
        userEmail = jwtService.extractUsername(jwt);//SecurityContextHolder 中的 Authentication 为空时,才进行处理  if(userEmail !=null&&SecurityContextHolder.getContext().getAuthentication()==null){//获取用户信息  UserDetails userDetails =this.userDetailsService.loadUserByUsername(userEmail);//从数据库中查询Token并判断Token状态是否正常  var isTokenValid = tokenRepository.findByToken(jwt).map(t ->!t.isExpired()&&!t.isRevoked()).orElse(false);//如果Token有效,并且Token状态正常,将用户信息存储到SecurityContextHolder  if(jwtService.isTokenValid(jwt, userDetails)&& isTokenValid){UsernamePasswordAuthenticationToken authToken =newUsernamePasswordAuthenticationToken(  
                userDetails,//用户信息  null,  
                userDetails.getAuthorities()//用户的权限  );  
                authToken.setDetails(newWebAuthenticationDetailsSource().buildDetails(request)//访问信息  );//将用户信息以及权限保存到 SecurityContextHolder的上下文中,方便后续使用  //eg: 获取当前用户id,获取当前用户权限等等  SecurityContextHolder.getContext().setAuthentication(authToken);}}  
        filterChain.doFilter(request, response);}}

上述代码主要逻辑为: 从请求头中获取到

token

.验证

token

的有效性并解析

token

中的信息存储到

SecurityContextHolder

上下文中,方便后续的使用.

3.6 退出登录

登录

以及

token

的校验已经说过了,现在就差一个退出登录了.大家是否还记得我们之前配置过一个

退出登录

的请求路径:

/api/v1/auth/logout

.当我们请求请求这个路径的时候,

security

会帮我们找到对应的

LogoutHandler

,然后调用

logout

方法实现退出登录.

@Service@RequiredArgsConstructorpublicclassLogoutServiceimplementsLogoutHandler{privatefinalTokenRepository tokenRepository;@Overridepublicvoidlogout(HttpServletRequest request,HttpServletResponse response,Authentication authentication  
    ){//从请求头中获取鉴权信息  finalString authHeader = request.getHeader("Authorization");finalString jwt;if(authHeader ==null||!authHeader.startsWith("Bearer ")){return;}//接续出token  
        jwt = authHeader.substring(7);//从数据库中查询出token信息  var storedToken = tokenRepository.findByToken(jwt).orElse(null);if(storedToken !=null){//设置token过期  
            storedToken.setExpired(true);  
            storedToken.setRevoked(true);  
            tokenRepository.save(storedToken);//清除SecurityContextHolder上下文  SecurityContextHolder.clearContext();}}}
security

帮我们做了很多的事情,我们只需要把

token

置为失效状态,然后清除掉

SecurityContextHolder

上下文,就解决了全部的问题

4 鉴权

下面通过几个例子,来讲解两种不同的鉴权配置方式

4.1 controller

@RestController@RequestMapping("/api/v1/admin")@PreAuthorize("hasRole('ADMIN')")//用户需要ADMIN角色才能访问  publicclassAdminController{@GetMapping@PreAuthorize("hasAuthority('admin:read')")//用户需要admin:read权限才能访问  publicStringget(){return"GET:: admin controller";}@PostMapping@PreAuthorize("hasAuthority('admin:create')")//用户需要admin:create权限才能访问  @HiddenpublicStringpost(){return"POST:: admin controller";}@PutMapping@PreAuthorize("hasAuthority('admin:update')")@HiddenpublicStringput(){return"PUT:: admin controller";}@DeleteMapping@PreAuthorize("hasAuthority('admin:delete')")@HiddenpublicStringdelete(){return"DELETE:: admin controller";}}

4.2 配置文件

下面贴出

SecurityConfiguration

配置类的部分代码

在这里插入图片描述


本文转载自: https://blog.csdn.net/weixin_55756734/article/details/133082139
版权归原作者 尘缘.. 所有, 如有侵权,请联系我们删除。

“Spring Boot 3 + JWT + Security 联手打造安全帝国:一篇文章让你掌握未来!”的评论:

还没有评论