Spring Boot 3 Security 6.2 JWT 认证/授权实现

教程分享 > Java教程 > Spring (126) 2024-08-19 17:28:24

前言

使用Spring Boot 3 Security 6.2 JWT 完成无状态的REST接口认证和授权管理。

环境

  • JDK 17
  • Spring Boot 3.3.2
  • Spring Security 6.3.1
  • Hutool 5.8.31

项目结构

Spring Boot 3 Security 6.2 JWT 认证/授权实现_图示-bd6e029c10f1436b81c9893e9811659e.png
项目结构图参考

项目Maven依赖

maven pom.xml

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.31</version>
</dependency>
</dependencies>

 

业务实现

 

创建认证授权需要的三个表和实体

SystemUserEntity表/实体 

用于存放认证需要的基础数据和自己业务的扩展数据

/**
* 模拟你数据库表对象实体
*/
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Data
public class SystemUserEntity {
//id
private String id;
//账号
private String username;
//密码
private String password;
//角色
private List<Integer> roles;
// ----其他你业务需要的字段---
}

PrivilegeEntity 表/实体 

用于存放权限数据,一般权限数据在开发时候初始化入库。

/**
* 权限数据库实体模拟
*
*/
@Data
@Builder
public class PrivilegeEntity {
private Integer id;//id
private String name;//名称
private String code;//编码
}

RoleEntity 表/实体

角色表,可以配合Spring Security 的ROLE_XX实现角色管理。一般情况下用的少,一般用细粒度的权限来组合成一个角色而不是直接使用角色。

/**
* 角色模拟数据库实体
*/
@Data
@Builder
public class RoleEntity {
private Integer id;
private String name;//名称
private String code;//编码
private List<PrivilegeEntity> privileges;//角色权限
}

提示:如果需要使用spring security的角色和权限双维度控制,简易角色编码保留ROLE_开头,以便区分角色和权限编码(后续会讲到角色和权限编码会放在一个集合里面“混用”)。

UserService用户业务处理

UserService 主要处理用户信息从数据库中拿出来。如用户基础信息/用户角色权限信息

public interface UserService {
/**
* 通过账号找用户
* @param username 账号
* @return 用户
*/
SystemUserEntity findByUsername(String username);


/**
* 角色信息查询
* @param ids
* @return
*/
List<RoleEntity> getRolesById(List<Integer> ids);

/**
* 用户注册模拟
* @param username 账号
* @param password 密码
*/
void register(String username, String password);

/**
* 用户登录,成功后返回token
* @param username
* @param password
* @return
*/
String login(String username, String password);

实现类

@Service
@Slf4j
public class UserServiceImpl implements UserService {

@Resource
PasswordEncoder passwordEncoder;

@Resource
AuthenticationManager authenticationManager;

@Resource
SecurityJwtService securityJwtService;

/**
* 以下部分使用map模拟数据库操作
*
*/
//id->object
final static ConcurrentHashMap<String,SystemUserEntity> database = new ConcurrentHashMap<>();
final static ConcurrentHashMap<Integer, RoleEntity> roles = new ConcurrentHashMap<>();
private static final AtomicInteger roleIdGenerator = new AtomicInteger(0);

/**
* 初始化几个数据
* 实际业务使用数据库动态配置
*/
static {
//权限角色初始化几个
RoleEntity roleUser = RoleEntity.builder()
.id(roleIdGenerator.incrementAndGet())
.code("ROLE_USER")
.privileges(List.of(
PrivilegeEntity.builder().code("user:userInfo").build()
))
.build();
roles.put(roleUser.getId(), roleUser);
RoleEntity roleAdmin = RoleEntity.builder()
.id(roleIdGenerator.incrementAndGet())
.code("ROLE_ADMIN")
.privileges(List.of(
PrivilegeEntity.builder().code("admin:info").build()
))
.build();
roles.put(roleAdmin.getId(), roleAdmin);

//用户初始化几个
SystemUserEntity admin = SystemUserEntity.builder()
.id(UUID.randomUUID().toString())
.username("admin")
//密码123456
.password("$2a$10$U/J6K1YB27tmCQnzUDb9tOh/wlOLzY3aQuiWG7WMCv94gcSSU1fY.")
.roles(List.of(2))
.build();
SystemUserEntity user = SystemUserEntity.builder()
.id(UUID.randomUUID().toString())
.username("user")
//密码123456
.password("$2a$10$U/J6K1YB27tmCQnzUDb9tOh/wlOLzY3aQuiWG7WMCv94gcSSU1fY.")
.roles(List.of(1))
.build();
database.put(admin.getId(), admin);
database.put(user.getId(), user);
}

@Override
public SystemUserEntity findByUsername(String username) {
Optional<Map.Entry<String, SystemUserEntity>> first = database.entrySet().stream().filter(e -> e.getValue().getUsername().equals(username)).findFirst();
return first.map(Map.Entry::getValue).orElse(null);
}

@Override
public List<RoleEntity> getRolesById(List<Integer> ids) {
List<RoleEntity> roleEntityList = new ArrayList<>();
roles.values().stream().filter(o->ids.contains(o.getId())).forEach(roleEntityList::add);
return roleEntityList;
}

@Override
public void register(String username, String password) {
SystemUserEntity byUsername = findByUsername(username);
if (Objects.nonNull(byUsername)){
throw new IllegalArgumentException("账号已经被注册,请换一个");
}
SystemUserEntity build = SystemUserEntity.builder()
.username(username)
.password(passwordEncoder.encode(password))
.id(UUID.fastUUID().toString())
.build();
database.put(build.getId(),build);
}

@Override
public String login(String username, String password) {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(username, password)
);
SystemUserEntity byUsername = findByUsername(username);
Objects.requireNonNull(byUsername,"账号不存在");
CustomUserDetail userDetail = CustomUserDetail.builder()
.systemUser(byUsername)
.build();
return securityJwtService.generateToken(username);
}
}

提示:这里以map操作代替了数据库操作,简化演示核心学习spring security相关内容,如需数据库相关教程请在站内搜索。

 

Spring Security 自定义用户载入相关处理

CustomUserDetail

自定义UserDetails 实现,满足Spring Security载入用户且可以方便扩展登录用户的相关信息

/**
* 注意:通过实现接口UserDetails来自定义登录用户需要定义password字段<br/>
* 通过继续<code>org.springframework.security.core.userdetails.User</code>实现自定义则不需要,或者直接使用该类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CustomUserDetail implements UserDetails {

private SystemUserEntity systemUser;

private List<GrantedAuthority> authorities;
//必须定义该字段,否则启动报错
private String password;

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
if (authorities != null) {
return authorities;
}
return List.of();
}

@Override
public String getPassword() {
return this.systemUser.getPassword();
}

@Override
public String getUsername() {
return this.systemUser.getUsername();
}

//根据业务来是否需要配置,配置则从数据库用户中获取状态返回出来
@Override
public boolean isAccountNonExpired() {
return UserDetails.super.isAccountNonExpired();
}

//根据业务来是否需要配置,配置则从数据库用户中获取状态返回出来
@Override
public boolean isAccountNonLocked() {
return UserDetails.super.isAccountNonLocked();
}

//根据业务来是否需要配置,配置则从数据库用户中获取状态返回出来
@Override
public boolean isCredentialsNonExpired() {
return UserDetails.super.isCredentialsNonExpired();
}

//根据业务来是否需要配置,配置则从数据库用户中获取状态返回出来
@Override
public boolean isEnabled() {
return UserDetails.super.isEnabled();
}
}

 

CustomUserDetailsService

用于实现 Spring Security 框架载入登录用户凭据。通过调用用户的基础业务来获取包括角色信息和权限信息等填充到Spring Security的凭据中。

@Slf4j
@Component
public class CustomUserDetailsService implements UserDetailsService {

@Resource
UserService userService;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SystemUserEntity byUsername = userService.findByUsername(username);
if (byUsername == null) {
return null;
}

List<GrantedAuthority> authorities = new ArrayList<>();
//找角色/权限
if (CollectionUtil.isNotEmpty(byUsername.getRoles())) {
List<RoleEntity> rolesById = userService.getRolesById(byUsername.getRoles());
List<String> privileges=new ArrayList<>();
for (RoleEntity role : rolesById) {
if (StrUtil.isNotEmpty(role.getCode())){
privileges.add(role.getCode());
}
List<PrivilegeEntity> rolePrivileges = role.getPrivileges();
if (CollectionUtil.isNotEmpty(rolePrivileges)) {
privileges.addAll(rolePrivileges.stream().map(PrivilegeEntity::getCode).toList());
}
}

for (String privilege : privileges) {
authorities.add(new SimpleGrantedAuthority(privilege));
}

}
return CustomUserDetail.builder()
.systemUser(byUsername)
.authorities(authorities)
.build();
}


}

 

JWT 相关处理

JwtProvider 组件

用于提供jwt相关的操作,基于hutool工具,你也可以替换为你自己喜欢的jwt工具

@Component
public class JwtProvider {

//模拟jwt加密密码
final static String SECRET_KEY="123456";

//生成token
public String generateToken(String account) {
Map<String,Object> payload=new HashMap<>();
Date signDate=new Date();
//--------------------------通用数据处理-----------------------
payload.put(JWTPayload.SUBJECT, account);
//签发时间
payload.put(JWTPayload.ISSUED_AT, signDate);
//生效时间
payload.put(JWTPayload.NOT_BEFORE, signDate);
//过期时间 最大有效期1天强制过期
payload.put(JWTPayload.EXPIRES_AT, DateUtil.offsetDay(signDate,1));
//--------------------------通用数据处理-----------------------
//--------------------以下部分可以设置一些自己的数据--------------
payload.put("userId","111");
return JWTUtil.createToken(payload, JWTSignerUtil.hs256(SECRET_KEY.getBytes(StandardCharsets.UTF_8)));

}

public String getLoginUserAccount(HttpServletRequest request) {
String token = getBearerToken(request);
if (StrUtil.isEmpty(token)) {
throw new AuthenticationCredentialsNotFoundException("token is empty !");
}
JWT jwt = verifyToken(token);
if (Objects.isNull(jwt)) {
throw new BadCredentialsException("token invalid !");
}
return jwt.getPayload().getClaimsJson().getStr(JWT.SUBJECT);
}

public String getBearerToken(HttpServletRequest request) {
String authorization = request.getHeader("Authorization");
if (authorization != null && authorization.startsWith("Bearer ")) {
return authorization.substring(7);
}
return null;
}

//校验签名有效+未过期
public JWT verifyToken(String token) {
JWT jwt = JWTUtil.parseToken(token);
boolean validate = jwt.setKey(SECRET_KEY.getBytes(StandardCharsets.UTF_8)).validate(0);
if (!validate) {
return null;
}
return jwt;
}
}

JWT AUTH Filter JWT过滤器

jwt过滤器在这里主要充当Spring Security认证的逻辑.

@Slf4j
@Component
@RequiredArgsConstructor
public class JwtAuthFilter extends OncePerRequestFilter {

@Resource
JwtProvider jwtProvider;
@Resource
CustomUserDetailsService customUserDetailsService;

@Resource
AppConfig appConfig;

//此处声明异常全局处理
@Resource
private HandlerExceptionResolver handlerExceptionResolver;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String requestURI = request.getRequestURI();

//忽略地址处理
List<String> ignoreUrls = appConfig.getAuth().getIgnoreUrls();
if (ignoreUrls.stream().anyMatch(e->e.matches(requestURI))) {
filterChain.doFilter(request, response);
return;
}

try {
String loginUserAccount = jwtProvider.getLoginUserAccount(request);
if (SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = customUserDetailsService.loadUserByUsername(loginUserAccount);
if (Objects.isNull(userDetails)){
throw new BadCredentialsException("username is invalid");
}
SecurityContext context = SecurityContextHolder.createEmptyContext();
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
context.setAuthentication(authToken);
SecurityContextHolder.setContext(context);
}else {
log.warn("SecurityContextHolder.getContext().getAuthentication() not null");
}
}catch (Exception e){
//丢给自定义全局异常处理器GlobalExceptionHandler,必须手动丢,否则全局异常无法捕获Filter级别的异常
handlerExceptionResolver.resolveException(request, response, null, e);
return;
}

filterChain.doFilter(request, response);
}
}

 

全局响应VO Result 

@Data
public class Result <T>{
private int code;
private String msg;
private T data;

public static <T> Result<T> success(T data){
Result<T> result = new Result<T>();
result.setCode(200);
result.setMsg("success");
result.setData(data);
return result;
}
public static Result<Void> fail(int code, String msg){
Result<Void> result = new Result<Void>();
result.setCode(code);
result.setMsg(msg);
return result;
}

public static Result<Void> fail(String msg){
Result<Void> result = new Result<Void>();
result.setCode(400);
result.setMsg(msg);
return result;
}
}
 

全局异常处理

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {


@ExceptionHandler(value = {BindException.class,MethodArgumentNotValidException.class})
public Result<?> methodArgumentNotValidException(Exception e, HttpServletRequest request){
List<FieldError> fieldErrors= new ArrayList<>();
if (e instanceof BindException){
fieldErrors=((BindException) e).getFieldErrors();
}
List<String> errorMsg = new ArrayList<>();
for (FieldError error : fieldErrors) {
errorMsg.add("["+error.getField()+"]"+error.getDefaultMessage());
}
String errMsg = String.join(",", errorMsg);
log.error("请求地址:{},参数校验错误 :{}",request.getRequestURI(),errMsg);
return Result.fail(400, errMsg);
}


@ExceptionHandler({MethodArgumentTypeMismatchException.class,HttpMessageNotReadableException.class})
public Object MethodArgumentTypeMismatchException(Exception e, HttpServletRequest request){
log.error("请求地址:{},参数格式或类型错误:{}",request.getRequestURI(),e.getMessage(),e);
return Result.fail(400,"参数格式或类型错误");
}

@ExceptionHandler({AuthenticationException.class})
public Result<?> authenticationException(AuthenticationException authenticationException,HttpServletRequest request){
log.error("请求地址:{},未登录或令牌无效:{}",request.getRequestURI(),authenticationException.getMessage(),authenticationException);
return Result.fail(401,"未登录或令牌无效");
}

@ExceptionHandler({AccessDeniedException.class})
public Result<?> accessDeniedException(AccessDeniedException e,HttpServletRequest request){
log.error("请求地址:{},未授权访问:{}",request.getRequestURI(),e.getMessage(),e);
return Result.fail(403,"未授权访问");
}

@ExceptionHandler({Exception.class})
public Result<?> exception(Exception e, HttpServletRequest request){
log.error("请求地址:{},未知异常:{}",request.getRequestURI(),e.getMessage(),e);
return Result.fail(500,e.getMessage());
}

}

这里主要关注 401  403的处理。

 

Spring Security 核心配置

/**
* <code>@EnableMethodSecurity</code>启用注解权限支持 @Secured / @PreAuthorize<br/>
* <p>
* <code>@Secured("ROLE_ADMIN")</code>
* <code>@PreAuthorize("hasRole('ROLE_ADMIN')")</code>有角色(两种方式)<br/>
* <code>@PreAuthorize("hasAnyRole({'ROLE_USER','ROLE_ADMIN'})")</code> 有任一角色<br/>
* <code>@PreAuthorize("hasAnyAuthority({'user:search','user:edit'})")</code>有任一权限<br/>
* </p>
*/
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig {
/**
* 登出处理
*/
@Resource
JwtAuthFilter jwtAuthFilter;

@Resource
CustomUserDetailsService customUserDetailsService;

@Resource
AppConfig appConfig;

/**
*
*/
@Resource
private AuthenticationConfiguration authenticationConfiguration;


@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
//关闭csrf
.csrf(AbstractHttpConfigurer::disable)
// 开启csrf 保护
// .csrf(Customizer.withDefaults())
// 认证失败处理类
// .exceptionHandling(handler->handler.authenticationEntryPoint(new Http403ForbiddenEntryPoint()))
//登初处理REST 可以不处理。也可以自定义一个标记缓存过期
// .logout(logout->logout.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()))
.authorizeHttpRequests(authorizeRequests ->{
AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry =
authorizeRequests.requestMatchers("/api/v2/auth/**").permitAll();
if (CollectionUtil.isNotEmpty(appConfig.getAuth().getIgnoreUrls())){
appConfig.getAuth().getIgnoreUrls().forEach(url -> {
registry.requestMatchers(url).permitAll();
});
}
registry.anyRequest().authenticated();
//以下为固定配置参考
// authorizeRequests
// //提示: anonymous permitAll 都表示不需要登录就可以访问。区别在于anonymous匿名访问已经登录则无法访问。
// .requestMatchers("/api/v1/auth/**").permitAll()
// .requestMatchers("/doc.html").permitAll()
// .requestMatchers("/login").permitAll()//不需要验证的
// .requestMatchers("/register").permitAll()
// .requestMatchers("/captcha").permitAll()
// .anyRequest().authenticated();//其他都要认证
}


)
//不通过Session获取SecurityContext
.sessionManagement(manager -> manager.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
//设定认证过滤器和过滤器顺序在UsernamePasswordAuthenticationFilter前面
//注意:该过滤器需要处理认证相关的逻辑和异常/错误处理,否则spring以匿名登录方式进入,spring security基本就只负责授权相关验证了
.authenticationProvider(authenticationProvider()).addFilterAfter(jwtAuthFilter,UsernamePasswordAuthenticationFilter.class)
;
return http.build();
}



@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config)
throws Exception {
return config.getAuthenticationManager();
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(customUserDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}

}

 

//忽略处理地址相关配置

@Component
@ConfigurationProperties(prefix = "app")
@Data
public class AppConfig {

@Resource
Auth auth;

@Component
@ConfigurationProperties(prefix = "app.auth")
@Data
public static class Auth{
/**
* 忽略授权地址
*/
private List<String> ignoreUrls = new ArrayList<>();
}
}

application.yml增加配置


app:
auth:
ignore-urls:
- /api/v1/auth/login
- /api/v1/auth/register

UserController 示例

前面配置已经完成了整个spring security相关设置,下面是通过UserController 用户登录/注册/获取信息接口进行测试

@RestController
public class UserController {

@Resource
private UserService userService;

@PostMapping("/api/v1/auth/register")
public Result<Void> register(String username, String password) {
userService.register(username, password);
return Result.success(null);
}

@PostMapping("/api/v1/auth/login")
public Result<String> login(String username, String password) {
return Result.success(userService.login(username, password));
}

// @Secured("ROLE_ADMIN")
@PreAuthorize(value = "hasAnyAuthority({'admin:info'})")
@PostMapping("/api/v1/user/getUserInfo")
public Result<SystemUserEntity> getUserInfo() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Objects.requireNonNull(authentication,"authentication is null");
CustomUserDetail customUserDetail = (CustomUserDetail) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
SystemUserEntity byUsername = userService.findByUsername(customUserDetail.getUsername());
return Result.success(byUsername);
}
}

 

测试验收结果

1.登录测试

测试预期结果:输入错误的账户密码提示错误/ admin/111111

Spring Boot 3 Security 6.2 JWT 认证/授权实现_图示-506ad7b22fa6400ba194faf2c025e9a6.png

测试预期结果:输入正确的账户密码返回登录成功的token值 admin/123456

Spring Boot 3 Security 6.2 JWT 认证/授权实现_图示-25200f445deb45d59889be67d161a7ef.png

测试结果:正常情况和错误情况均与预期结果一致,测试通过。

2.认证访问测试

认证访问测试即:接口只要是登录了都能访问。

首先把测试接口调整成下面的样子(编辑代码后均需重启idea

代码聚焦

//    @Secured("ROLE_ADMIN")
// @PreAuthorize(value = "hasAnyAuthority({'admin:info'})")
@PostMapping("/api/v1/user/getUserInfo")
public Result<SystemUserEntity> getUserInfo() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Objects.requireNonNull(authentication,"authentication is null");
CustomUserDetail customUserDetail = (CustomUserDetail) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
SystemUserEntity byUsername = userService.findByUsername(customUserDetail.getUsername());
return Result.success(byUsername);
}

去掉所有权限注解,访问该接口仅需登录即可

测试预期结果:未携带token或token问题,返回401 未认证

Spring Boot 3 Security 6.2 JWT 认证/授权实现_图示-495a7a58372048a586b651072ae7d91e.png
未携带token测试

测试预期结果:携带正确的token接口正常返回数据

Spring Boot 3 Security 6.2 JWT 认证/授权实现_图示-7225be0cdf6f4baf94b4d225372b3f46.png
携带bearer 格式token

测试结果:未携带token和携带token执行结果与预期一致,测试通过。

 

3.授权访问测试

授权访问是在认证的基础上添加了角色或者权限的校验,需满足角色或权限校验结果才能正常访问,否则返回403未授权

 

角色授权测试

编辑测试接口,需要ROLE_ADMIN才能访问(编辑代码后均需重启idea

    @Secured("ROLE_ADMIN")
// @PreAuthorize(value = "hasAnyAuthority({'admin:info'})")
@PostMapping("/api/v1/user/getUserInfo")
public Result<SystemUserEntity> getUserInfo() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Objects.requireNonNull(authentication,"authentication is null");
CustomUserDetail customUserDetail = (CustomUserDetail) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
SystemUserEntity byUsername = userService.findByUsername(customUserDetail.getUsername());
return Result.success(byUsername);
}

提示:前面我们已经初始化了两个用户user和admin

测试预期结果:使用用户user/123456 token访问该接口返回403

Spring Boot 3 Security 6.2 JWT 认证/授权实现_图示-7ab262bb3f1e4695b1774c997748a621.png
user用户测试

测试预期结果:使用用户admin/123456 token访问该接口返回正常数据

Spring Boot 3 Security 6.2 JWT 认证/授权实现_图示-1e6209056a5e4ac2b732316a0adb4744.png
admin用户测试

测试结果:user账户和admin账户测试结果均与预期结果一致。

权限授权测试

编辑接口,设定admin:info权限访问 (编辑代码后均需重启idea

代码聚焦

//    @Secured("ROLE_ADMIN")
@PreAuthorize(value = "hasAnyAuthority({'admin:info'})")
@PostMapping("/api/v1/user/getUserInfo")
public Result<SystemUserEntity> getUserInfo() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Objects.requireNonNull(authentication,"authentication is null");
CustomUserDetail customUserDetail = (CustomUserDetail) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
SystemUserEntity byUsername = userService.findByUsername(customUserDetail.getUsername());
return Result.success(byUsername);
}

 

测试预期结果:使用user账户访问返回403(user用户没有这个权限编码)

Spring Boot 3 Security 6.2 JWT 认证/授权实现_图示-451dafd53f4d455bb82d239cf083ab39.png
user用户测试

测试预期结果:使用admin账户访问成功返回数据(admin用户的权限有这个编码)

Spring Boot 3 Security 6.2 JWT 认证/授权实现_图示-cf45449335b748a085ce20881caaeb4c.png
admin用户测试

测试结果:user用户和admin用户测试结果均与预期一致,测试通过。

 

 

https://www.leftso.com/article/2408191444549714.html

相关文章
前言使用Spring Boot 3 Security 6.2 JWT 完成无状态的REST接口认证和授权管理。环境JDK 17Spring Boot 3.3.2
环境JDK 17Spring Boot 3.2.1-3.2.3Spring Security 6.2.1-6.3.1Spring Security 权限/角色常
spring boot 2.0 security 5.0 整合,实现自定义表单登录。spring boot 2.0框架使用。
Spring Security 配置多个Authentication Providers认证器
解决spring security 整合到spring boot中,UserDetailsService接口的loadUserByUsername方法参数username输入为空问题。一  检查...
spring security常用注解@Secured、@PreAuthorize 、@PostAuthorize说明,Java编程,spring security
spring boot 入门之security oauth2 jwt完美整合例子,Java编程中spring boot框架+spring security框架+spring security o...
引言在这篇文章中,我们将讨论如何使用Spring Boot Security OAuth2保护REST API
1.概述本文继续使用spring boot 和Spring Security系列进行注册,并着重于如何正确实现角色和权限
引言    通过之前spring boot mybatis 整合的讲解: spring boot mybaties整合  (spring boot mybaties 整合 基于Java注解方式写...
Spring Boot 2.0,Spring框架的Spring Boot 中的Spring Boot Actuator变化讲解。并且了解如何在Spring Boot 2.0中使用Actuator...
Java编程中spring security4是一个spring框架项目的一个安全方面的项目。主要用于用户认证,授权,角色认证
引言在本文中,我们将讨论有关Spring启动安全性和JWT令牌的OAUTH2实现以及保护REST API
spring boot 入门之spring session实现restful apis。通过spring boot或者spring mvc整合spring session的方式来实现sessio...