SpringSecurity基础学习-权限认证

SpringSecurity除了身份认证之外,还支持授权管理,其授权维度分为两类:role、authority

编写demo

  • 测试用api
package com.learn.security.api.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author PC
 * 权限测试Controller
 */
@RestController("authController.v1")
@RequestMapping("/v1/auth")
public class AuthController {

    @GetMapping("/admin")
    public String admin(){
        return "hello admin";
    }

    @GetMapping("/role-admin")
    public String roleAdmin(){
        return "hello ROLE_admin";
    }

    @GetMapping("/pub")
    public String pub(){
        return "hello pub";
    }

}
  • UserDetailsServiceImpl#loadUserByUsername中添加授权代码
@Override
public UserDetails loadUserByUsername(String loginName) throws UsernameNotFoundException {
    // 1. 查询用户
    Login loginInfo = loginService.getLoginInfo(loginName);
    if (loginInfo == null) {
        //这里找不到必须抛异常
        throw new UsernameNotFoundException("User " + loginName + " was not found in db");
    }

    //2. 获取并加密密码
    Assert.isTrue(StringUtils.isNotEmpty(loginInfo.getPassword()), "password deletion");
    String password = passwordEncoder.encode(loginInfo.getPassword());

    //3. 赋予user账户一个或多个权限,用逗号分隔,测试可以使用用户名,正式需添加权限字段
    List<GrantedAuthority> grantedAuthorityList = AuthorityUtils.commaSeparatedStringToAuthorityList(loginName);

    return new User(loginName, password, grantedAuthorityList);
}
  • 添加未授权页面un-auth.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>未授权</title>
</head>
<body>
<h1>您未获得授权,请重新<a href="/login.html">登录</a></h1>
</body>
</html>
  • SecurityAutoConfiguration中添加权限配置
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
            //自定义登录页面
            .formLogin()
            //登陆页面设置
            .loginPage("/login.html")
            //登录url设置
            .loginProcessingUrl("/user/login")
            //登录成功后跳转的路径,如果希望跳回原路径,alwaysUse不填或填false
            .defaultSuccessUrl("/success.html")
            //允许访问
            .permitAll()
            //未获得授权跳转的页面
            .and().exceptionHandling().accessDeniedPage("/un-auth.html")
            //设置认证权限
            .and().authorizeRequests()
            //该接口需要admin权限
            .antMatchers("/v1/auth/admin").hasAuthority("admin")
            //该接口需要ROLE_admin角色
            .antMatchers("/v1/auth/role-admin").hasRole("admin")
            //该接口为公开接口
            .antMatchers("/v1/auth/pub").permitAll()
            .anyRequest().authenticated()
            //关闭csrf防护
            .and().csrf().disable();
    return http.build();
}

测试

会跳到未授权页面

可正常返回

角色前缀变更

默认前缀分析

通过测试,我们可以看到,在SecurityAutoConfiguration中,仅配置了antMatchers("/v1/auth/role-admin").hasRole("admin"),并没有指定ROLE_admin,那么这个前缀是在哪配置的呢?

进入hasRole方法,其代码如下

public ExpressionUrlAuthorizationConfigurer<H>.ExpressionInterceptUrlRegistry hasRole(String role) {
    return this.access(ExpressionUrlAuthorizationConfigurer.hasRole(ExpressionUrlAuthorizationConfigurer.this.rolePrefix, role));
}

从中可以发现,有一个this.rolePrefix,ExpressionUrlAuthorizationConfigurer有一个构造方法,设置了rolePrefix的值,其代码如下

public ExpressionUrlAuthorizationConfigurer(ApplicationContext context) {
    String[] grantedAuthorityDefaultsBeanNames = context.getBeanNamesForType(GrantedAuthorityDefaults.class);
    if (grantedAuthorityDefaultsBeanNames.length == 1) {
        GrantedAuthorityDefaults grantedAuthorityDefaults = (GrantedAuthorityDefaults)context.getBean(grantedAuthorityDefaultsBeanNames[0], GrantedAuthorityDefaults.class);
        this.rolePrefix = grantedAuthorityDefaults.getRolePrefix();
    } else {
        this.rolePrefix = "ROLE_";
    }

    this.REGISTRY = new ExpressionInterceptUrlRegistry(context);
}

从中可以发现,如果没有进行特殊配置,rolePrefix就被赋值为ROLE_

修改前缀

分析出了默认前缀的来源,接下来我们就可以自定义前缀,从代码中可知,如果想要自定义前缀,需要初始化GrantedAuthorityDefaults类,而前缀在代码中写死也不太方便,因此还需要支持在yml中配置

  • SecurityConfigProperties
package com.learn.security.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * @author PC
 * Security配置属性
 */
@Data
@Configuration
@ConfigurationProperties("cus.security")
public class SecurityConfigProperties {

    private String rolePrefix = "ROLE_";
}
  • SecurityAutoConfiguration
@Bean
public GrantedAuthorityDefaults grantedAuthorityDefaults(){
    return new GrantedAuthorityDefaults(securityConfigProperties.getRolePrefix());
}
  • application.yml
cus:
  security:
    role_prefix: role_

测试

  • 为了测试自定义前缀,数据库中添加role_admin用户

使用授权注解

@Secured

若想使用注解,需要在启动类中先开启securedEnabled

@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityApplication {

    public static void main(String[] args) {
        SpringApplication.run(SecurityApplication.class, args);
    }

}

判断是否有角色,另外需要注意的是这里匹配的字符串需要添加前缀“role_”(注意,此处是我的自定义前缀,如果是默认的,则为ROLE_),如下所示,仅有当包含任一角色时才能访问

@Secured(value = {"admin"})
@GetMapping("/admin")
public String admin(){
    return "hello admin";
}

@Secured(value = {"role_admin"})
@GetMapping("/role-admin")
public String roleAdmin(){
    return "hello ROLE_admin";
}

上述代码和在初始授权时在SecurityAutoConfiguration类中配置的其实是一样的效果,多余的配置代码可以删除

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
            //自定义登录页面
            .formLogin()
            //登陆页面设置
            .loginPage("/login.html")
            //登录url设置
            .loginProcessingUrl("/user/login")
            //登录成功后跳转的路径,如果希望跳回原路径,alwaysUse不填或填false
            .defaultSuccessUrl("/success.html")
            //允许访问
            .permitAll()
            //配置登出
            .and().logout().logoutUrl("/logout").logoutSuccessUrl("/login.html").permitAll()
            //未获得授权跳转的页面
            .and().exceptionHandling().accessDeniedPage("/un-auth.html")
            //设置认证权限
            .and().authorizeRequests()
            .anyRequest().authenticated()
            //关闭csrf防护
            .and().csrf().disable();
    return http.build();
}

测试

无权访问

访问http://127.0.0.1:8888/v1/auth/role-admin

参考资料

[1]gitee项目仓库地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值