携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
回顾
上文对SpringSecurity登录认证部分做了分析处理,先回顾一下整体的思路流程
登录部分:
- 自定义登录接口信息:
- 通过调用ProviderManager的方法进行认证,如果认证通过jwt工具类生成对应的jwt信息
- 把相应的用户信息存入redis中
- 自定义UserDetailsService
- 通过数据库查询完成认证操作
校验部分
- 自定义jwt认证过滤器
- 获取token
- 通过jwt工具类解析对应的token获取用户名
- 通过用户名获取redis中用户的全部信息
- 存入SecurityContextHolder
今天继续完善流程中的授权部分
授权
在SpringSecurity中,会使用默认的FilterSecurityInterceptor来进行权限校验。在FilterSecurityInterceptor中会从SecurityContextHolder获取其中的Authentication,然后获取其中的权限信息。当前用户是否拥有访问当前资源所需的权限。
- 项目中只需要把当前登录用户的权限信息也存入Authentication。
- 设置资源所需要的权限。
具体实现
这里使用注解方式实现授权部分
SpringSecurity为我们提供了基于注解的权限控制方案,可以使用注解去指定访问对应的资源所需的权限。(类似Shiro的授权部分)
需要先开启相关配置
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
接口访问时可以使用注解@PreAuthorize完成接口授权
@PreAuthorize("hasAuthority('test')")
@RequestMapping(value = "/test")
public String test() {
return "success";
}
封装登录用户的权限信息
在UserDetailsServiceImpl中需要将对应的登录用户权限信息一并查出封装到UserDetails中返回
以下部分权限集合先写死替代,这块后续自己做的时候可以转为数据库查询,不再演示
对LoginUser完善权限部分
@Data
@NoArgsConstructor
public class LoginUser implements UserDetails {
private SysUser sysUser;
private List<String> permissions;
// 权限对应的集合信息
@JSONField(serialize = false)
private List<SimpleGrantedAuthority> authorities;
public LoginUser(SysUser sysUser, List<String> permissions) {
this.sysUser = sysUser;
this.permissions = permissions;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
if (authorities != null && authorities.size() > 0) {
return authorities;
}
authorities = permissions.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
return authorities;
}
@Override
public String getPassword() {
return sysUser.getPassword();
}
@Override
public String getUsername() {
return sysUser.getUsername();
}
@Override
public boolean isAccountNonExpired() {
// TODO 先置为true
return true;
}
@Override
public boolean isAccountNonLocked() {
// TODO 先置为true
return true;
}
@Override
public boolean isCredentialsNonExpired() {
// TODO 先置为true
return true;
}
@Override
public boolean isEnabled() {
// TODO 先置为true
return true;
}
}
修改UserDetailServiceImpl
@Service
public class UserDetailServiceImpl implements UserDetailsService {
@Resource
private SysUserMapper sysUserMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 查询用户信息
SysUser sysUser = sysUserMapper.selectOne(new QueryWrapper<SysUser>().lambda()
.eq(SysUser::getUsername, username));
if (Objects.isNull(sysUser)) {
throw new RuntimeException("用户名或密码不正确!");
}
// TODO 查询权限信息,目前写死状态,可更改为数据库查询
List<String> permissions = new ArrayList<>();
permissions.add("test");
permissions.add("admin");
// 数据封装为UserDetails
return new LoginUser(sysUser, permissions);
}
}
更改jwt过滤器中权限认证部分
@Component
public class JwtFilter extends OncePerRequestFilter {
@Resource
private SysUserMapper sysUserMapper;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 获取token
String token = request.getHeader("token");
if (!StringUtils.hasLength(token)) {
filterChain.doFilter(request, response);
return;
}
// 解析token
String username = JWTUtil.getUsername(token);
// 从redis中读取用户信息 TODO 目前从数据库读取
SysUser sysUser = sysUserMapper.selectOne(new QueryWrapper<SysUser>().lambda()
.eq(SysUser::getUsername, username));
// TODO 目前权限信息先写死,可从redis中读取
List<String> permissions = new ArrayList<>();
permissions.add("test");
permissions.add("admin");
LoginUser loginUser = new LoginUser(sysUser, permissions);
// 存入SecurityContextHolder
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
filterChain.doFilter(request, response);
}
}
结果演示
去掉对应的权限信息后访问结果为403无权限
SpringSecurity基本的入门登录认证授权流程大致就是这样,具体的实现方式有很多,这里只是列举其中的一个,大家可通过整个的认证授权流程,根据各自的需求实现对应的接口信息来完善,欢迎各位补充!!!