SpringSecurity快速入门(三)

133 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情

image.png

回顾

上文对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);
    }
}

结果演示

image.png

去掉对应的权限信息后访问结果为403无权限

image.png

SpringSecurity基本的入门登录认证授权流程大致就是这样,具体的实现方式有很多,这里只是列举其中的一个,大家可通过整个的认证授权流程,根据各自的需求实现对应的接口信息来完善,欢迎各位补充!!!

a8576e8eb0cbbe70d165506c0e6046dd.jpg