使用JWT保存权限以及使用Spring Security控制访问权限

使用JWT保存权限

UserDetailsServiceImpl中,调用的adminMapper.getLoginInfoByUsername()中已经包含用户的权限,则,在返回的UserDetails对象中封装权限信息:

UserDetails userDetails = User.builder()
                .username(loginAdmin.getUsername())
                .password(loginAdmin.getPassword())
                .accountExpired(false)
                .accountLocked(false)
                .credentialsExpired(false)
                .disabled(loginAdmin.getEnable() == 0)
                .authorities(loginAdmin.getPermissions().toArray(new String[] {})) // 调整
                .build();

AdminServiceImpl中,执行认证且成功后,返回的Authentication对象中的“当事人”就是以上返回的UserDetails对象,所以,此对象中是包含了以上封装的权限信息的,则可以将权限信息取出并封装到JWT中。

需要注意:如果直接将权限(Collection<? extends GrantedAuthority>)存入到JWT数据中,相当于把Collection<? extends GrantedAuthority>转换成String,此过程会丢失数据的原始类型,且不符合自动反序列化格式,后续解析时,无法直接还原成Collection<? extends GrantedAuthority>类型!为解决此问题,可以先将Collection<? extends GrantedAuthority>转换成JSON格式的字符串再存入到JWT中,后续,解析JWT时得到的也会是JSON格式的字符串,可以反序列化为Collection<? extends GrantedAuthority>格式!

则先添加JSON工具类的依赖项:

<!-- fastjson:实现对象与JSON的相互转换 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.75</version>
</dependency>

然后,在AdminServiceImpl中,先从认证成功的返回结果中取出权限,然后存入到JWT中:

// 从认证返回结果中取出当事人信息
User principal = (User) authenticateResult.getPrincipal();
String username = principal.getUsername();
log.debug("认证信息中的用户名:{}", username);
// ===== 以下是新增 ======
Collection<GrantedAuthority> authorities = principal.getAuthorities();
log.debug("认证信息中的权限:{}", authorities);
String authorityListString = JSON.toJSONString(authorities);
log.debug("认证信息中的权限转换为JSON字符串:{}", authorityListString);

// 生成JWT,并返回
// 准备Claims值
Map<String, Object> claims = new HashMap<>();
claims.put("username", username);
claims.put("authorities", authorityListString); // 新增

最后,在JwtAuthorizationFilter中,解析JWT时,取出权限的JSON字符串,将其反序列化为符合Collection<? extends GrantedAuthority>的格式:List<SimpleGrantedAuthority>,并用于存入到认证信息中:

String username = claims.get("username", String.class);
log.debug("从JWT中解析得到【username】的值:{}", username);
String authorityListString = claims.get("authorities", String.class); // 新增
log.debug("从JWT中解析得到【authorities】的值:{}", authorityListString); // 新增

// 准备权限,将封装到认证信息中
List<SimpleGrantedAuthority> authorityList
        = JSON.parseArray(authorityListString, SimpleGrantedAuthority.class);

// 准备存入到SecurityContext的认证信息
Authentication authentication
        = new UsernamePasswordAuthenticationToken(
        		username, null, authorityList);

使用Spring Security控制访问权限

首先,需要在Spring Security的配置类上开启方法前的权限检查:

@Slf4j
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) // 新增
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    // 省略配置类中原有代码
}

然后,在需要对权限进行检查(控制)的控制器类的方法上,使用注解来配置权限,例如:

// http://localhost:9081/admins
@ApiOperation("查询管理员列表")
@ApiOperationSupport(order = 400)
@PreAuthorize("hasAuthority('/ams/admin/read')") // 新增
@GetMapping("")
public JsonResult<List<AdminListItemVO>> list() {
    log.debug("开始处理【查询管理员列表】的请求……");
    List<AdminListItemVO> list = adminService.list();
    return JsonResult.ok(list);
}

以上新增的@PreAuthorize("hasAuthority('/ams/admin/read')")就表示已经通过认证的用户必须具有 '/ams/admin/read' 权限才可以访问此请求路径(http://localhost:9081/admins`),如果没有权限,将抛出`org.springframework.security.access.AccessDeniedException: 不允许访问

由于无操作权限时会出现新的异常,则在GlobalExceptionHandler中补充对此类异常的处理:

@ExceptionHandler
public JsonResult<Void> handleAccessDeniedException(AccessDeniedException e) {
    log.debug("处理AccessDeniedException");
    Integer serviceCode = ServiceCode.ERR_FORBIDDEN.getValue();
    String message = "请求失败,当前账号无此操作权限!";
    return JsonResult.fail(serviceCode, message);
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

香扑扑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值