一文详细讲解SpringBoot 动态权限校验实现方案

1背景
简单先说一下需求吧,这样也好让看的人知道到底适不适合自己。

实现自定义的登录认证。
登录成功,生成token并将token 交由redis管理。
登录后对用户访问的接口进行接口级别权限认证。
springSecurity提供的注解权限校验适合的场景是系统中仅有固定的几个角色,且角色的凭证不可修改(如果修改需要改动代码)。

@PreAuthorize(“hasAuthority(‘ROLE_TELLER’)”)
public Account post(Account account, double amount);
注:ROLE_TELLER是写死的。

后端系统的访问请求有以下几种类型:

登录、登出(可自定义url)
匿名用户可访问的接口(静态资源,demo示例等)
其他接口(在登录的前提下,继续判断访问者是否有权限访问)
2环境搭建
依赖引入,包括springSecurity、redis、redis session需要的依赖:

org.springframework.boot spring-boot-starter-security 2.3.4.RELEASE org.springframework.session spring-session-data-redis 2.3.1.RELEASE org.springframework.boot spring-boot-starter-data-redis 2.3.4.RELEASE 注:springBoot版本也是2.3.4.RELEASE,如果有版本对应问题,自行解决。有用到swagger,为了便于测试。

新建springSecurity配置类
新建 WebSecurityConfig.java 继承自 WebSecurityConfigurerAdapter,过滤匿名用户可访问的接口。

WebSecurityConfig作为springSecurity的主配置文件。

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

/**
 * Swagger等静态资源不进行拦截
 */
@Override
public void configure(WebSecurity web) {
    web.ignoring().antMatchers(
            "/*.html",
            "/favicon.ico",
            "/**/*.html",
            "/**/*.css",
            "/**/*.js",
            "/error",
            "/webjars/**",
            "/resources/**",
            "/swagger-ui.html",
            "/swagger-resources/**",
            "/v2/api-docs");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            //配置一些不需要登录就可以访问的接口
            .antMatchers("/demo/**", "/about/**").permitAll()
            //任何尚未匹配的URL只需要用户进行身份验证
            .anyRequest().authenticated()
            .and()
            .formLogin()//允许用户进行基于表单的认证
            .loginPage("/mylogin");
}

}
图片
注:证明可以访问静态资源不会被拦截

自定义登录认证
springSecurity是基于过滤器进行安全认证的。

我们需要自定义:

登录过滤器:负责过滤登录请求,再交由自定义的登录认证管理器处理。
登录成功处理类:顾名思义,登录成功后的一些处理(设置返回信息提示“登录成功!”,返回数据类型为json)。
登录失败处理类:类似登录成功处理类。Ps:登录成功处理类和失败处理类有默认的实现可以不自定义。但是建议自定义,因为返回的信息为英文,一般情况不符合要求。
登录认证管理器:根据过滤器传过来的登录参数,进行登录认证,认证后授权。
新建登录成功处理类
需要实现 AuthenticationSuccessHandler

@Component
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

private static final Logger LOGGER = LoggerFactory.getLogger(CustomAuthenticationSuccessHandler.class);

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
    response.setContentType(MediaType.APPLICATION_JSON_VALUE);
    response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
    //登录成功返回的认证体,具体格式在后面的登录认证管理器中
    String responseJson = JackJsonUtil.object2String(ResponseFactory.success(authentication));
    if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("登录成功!");
    }
    response.getWriter().write(responseJson);
}

}
新建登录失败处理类
实现 AuthenticationFailureHandler

@Component
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {

private static final Logger LOGGER = LoggerFactory.getLogger(CustomAuthenticationFailureHandler.class);

@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException {
    String errorMsg;
    if (StringUtils.isNotBlank(e.getMessage())) {
        errorMsg = e.getMessage();
    } else {
        errorMsg = CodeMsgEnum.LOG_IN_FAIL.getMsg();
    }
    response.setContentType(MediaType.APPLICATION_JSON_VALUE);
    response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
    String responseJson = JackJsonUtil.object2String(ResponseFactory.fail(CodeMsgEnum.LOG_IN_FAIL,errorMsg));
    if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("认证失败!");
    }
    response.getWriter().write(responseJson);
}

}
新建登录认证管理器

实现 AuthenticationProvider ,负责具体的身份认证(一般数据库认证,在登录过滤器过滤掉请求后传入)

@Component
public class UserVerifyAuthenticationProvider implements AuthenticationProvider {

private PasswordEncoder passwordEncoder;
@Autowired
private UserService userService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    String userName = (String) authentication.getPrincipal(); // Principal 主体,一般指用户名
    String passWord = (String) authentication.getCredentials(); //Credentials 网络凭证,一般指密码
    //通过账号去数据库查询用户以及用户拥有的角色信息
    UserRoleVo userRoleVo = userService.findUserRoleByAccount(userName);
    //数据库密码
    String encodedPassword = userRoleVo.getPassWord();
    //credentials凭证即为前端传入密码,因为前端一般用Base64加密过所以需要解密。
    String credPassword = new String(Base64Utils.decodeFromString(passWord), StandardCharsets.UTF_8);
    // 验证密码:前端明文,数据库密文
    passwordEncoder = new MD5Util();
    if (!passwordEncoder.matches(credPassword,
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

毕业小助手

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

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

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

打赏作者

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

抵扣说明:

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

余额充值