Spring Security介绍(四)权限校验

本文详细介绍了如何在SpringSecurity中使用注解式权限校验,包括@Secured,@PreAuthorize和@PostAuthorize的使用方法、区别以及自定义权限检查。同时讨论了权限异常处理和配置示例。
摘要由CSDN通过智能技术生成

用户认证+授权后,就可以进行接口权限控制。思路是拿用户(已授予的)权限与接口所需权限进行比较,不包含则视为无权。在SpringSecurity中,权限校验可以通过以下方式实现:

(1)自定义拦截器或者AOP校验:对需要鉴权的接口做拦截,用户的权限从SecurityContextHolder中获取,接口访问权限可以通过自定义Annotation注入,具体参照

拦截器与过滤器(三)拦截自定义Annotation注解_@annotation 拦截类注解-CSDN博客

(2) 注解式权限校验:

和shiro类似,sercurity也提供了注解式权限校验。

本篇主要看下注解式权限校验。

一、使用方法

Spring Security默认是禁用注解的,要想开启注解,需要加上@EnableMethodSecurity注解

使用@Secured需要在配置类中添加注解***@EnableGlobalMethodSecurity(securedEnabled=true)***才能生效
使用@PreAuthorize和@PostAuthorize需要在配置类中配置注解***@EnableGlobalMethodSecurity(prePostEnable=true)***才能生效

如我:

@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityWebConfig {
}

二、注解方法

即在注解中可以直接调用的方法。

1、hasAuthority(String):判断角色是否具有特定权限
http.authorizeRequests().antMatchers(“/main1.html”).hasAuthority(“admin”)

2、hasAnyAuthority(String …):如果用户具备给定权限中某一个,就允许访问
http.authorizeRequests().antMatchers(“/admin/read”).hasAnyAuthority(“xxx”,“xxx”)

3、hasRole(String):如果用户具备给定角色就允许访问,否则出现403
http.authorizeRequests().antMatchers(“/admin/read”).hasRole(“ROLE_管理员”)。

注意:hasRole与hasAuthority的区别:hasRole的值会添加ROLE_开头进行判断,而hasAuthority不会

4、hasAnyRole(String …):如果用户具备给定角色的任意一个,就允许被访问
http.authorizeRequests().antMatchers(“/guest/read”).hasAnyRole(“ROLE_管理员”, “ROLE_访客”)

5、hasIpAddress(String):请求是指定的IP就允许访问
http.authorizeRequests().antMatchers(“/ip”).hasIpAddress(“127.0.0.1”)

6、permitAll():允许所有人(可无任何权限)访问
7、denyAll():不允许任何(即使有最大权限)访问。
8、isAnonymous():为可匿名(不登录)访问。
9、isAuthenticated():为身份证认证后访问。
10、isRememberMe():为记住我用户操作访问。

11、isFullyAuthenticated():

为非匿名且非记住我用户允许访问

12、自定义校验方法
interface TestPermissionEvaluator {
    boolean check(Authentication authentication);
}

@Service("testPermissionEvaluator")
public class TestPermissionEvaluatorImpl implements TestPermissionEvaluator {

    public boolean check(Authentication authentication) {
        System.out.println("进入了自定义的匹配器" + authentication);
        return false;
    }
}
@PreAuthorize("@testPermissionEvaluator.check(authentication)")
public String test0() {
	return "说明你有自定义权限";
}

注意:如果注解要使用permitAll()、isAnonymous()等方法时,需要在config方法中取消.anyRequest().authenticated()的设置,否则会无效。

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors()
            .and()
            .csrf().disable();
        // 下面需注释,否则注解无效,生效的是这里的配置
            /*.authorizeRequests()
        	.anyRequest()
        	.authenticated(); */
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
        	.passwordEncoder(bCryptPasswordEncoder);
    }
}

三、spring security注解

Spring Security 支持三套注解:

#注解
jsr250 注解@DenyAll、@PermitAll、@RolesAllowed
secured 注解@Secured
prePost 注解@PreAuthorize、@PostAuthorize

具体来看下: 

1、@Secured

角色校验,请求到来访问控制单元方法时必须包含XX角色才能访问。使用方法:

(1)角色必须添加ROLE_前缀

(2)如果要求只有同时拥有admin和user的用户才能访问某个方法时,@Secured就无能为力了

@Component
public class UserDetailServiceImpl implements UserDetailsService {

    @Resource
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        if (username.equals("root")) {
            return new User(username, passwordEncoder.encode("123"), AuthorityUtils.createAuthorityList("ROLE_read"));
        } else if (username.equals("user")) {
            return new User(username, passwordEncoder.encode("123"), AuthorityUtils.createAuthorityList("ROLE_write"));
        }
        return new User(username, passwordEncoder.encode("123"), AuthorityUtils.createAuthorityList("read"));
    }
}
@RestController
public class HelloController {

    @RequestMapping("/read")
    @Secured(value = {"ROLE_read"})
    public String read() {
        return "read";
    }

    @RequestMapping("/write")
    @Secured(value = {"ROLE_write"})
    public String write() {
        return "write";
    }

	// 错误实例
    @RequestMapping("/read2")
    @Secured(value = {"read"})
    public String read2() {
        return "read2";
    }
}
2、@PreAuthorize

权限校验,请求到来访问控制单元之前必须包含XX权限才能访问,控制单元方法执行前进行角色校验。

@RestController
public class HelloController {

    @RequestMapping("/read3")
    @PreAuthorize(value = "hasRole('ROLE_read')")
    public String read3() {
        return "read3";
    }

    @RequestMapping("/read4")
    @PreAuthorize(value = "hasAnyRole('ROLE_read','ROLE_write')")
    public String read4() {
        return "read4";
    }

    @RequestMapping("/read5")
    @PreAuthorize(value = "hasAnyAuthority('ROLE_read','read')")
    public String read5() {
        return "read5";
    }
}
3、@PostAuthorize

权限校验,请求到来访问控制单元之后必须包含XX权限才能访问,在方法执行后进行权限校验,适合验证带有返回值的权限。

@PostAuthorize("hasRole('ROLE_管理员')")
@RequestMapping("/toMain")
public String toMain(){
    return "main";
}

@GetMapping("/helloUser")
@PostAuthorize("returnObject!=null && returnObject.username == authentication.name")
public User helloUser() {
    Object pricipal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    User user;
    if("anonymousUser".equals(pricipal)) {
        user = null;
    }else {
        user = (User) pricipal;
    }
    return user;
}
4、@PreFilter

对传递参数值做过滤

@PostMapping("/preFilter")
@PreAuthorize("hasAnyAuthority('admin','update')") // 注意单引号
@PreFilter("filterObject.id % 2 == 0") // id为偶数才能请求
public String preFilter(@RequestBody List<User> userLists){
    log.info("=== 进入当前 preFilter ====");
    log.info(userLists.toString());
    return "security test 5  preFilter  需要验证的接口";
}
5、@PostFilter

权限验证通过后,留下指定用户名的数据,对返回数据做过滤

@RequestMapping("/postFilter")
@PreAuthorize("hasAnyAuthority('admin','update')") // 注意单引号
@PostFilter("filterObject.username == 'xiangjiao'") // 针对返回数据做过滤
public List<User> postFilter(){
    log.info("=== 进入当前 postFilter ====");
    List<User> userLists = new ArrayList<>();
    userLists.add(new User(1,"xiangjiao","bunana",1,0));
    userLists.add(new User(2,"xiangjiao2","bunana2",1,0));
    return userLists;
}
6、JSR-250注解

@DenyAll、@PermitAll、@RolesAllowed,

使用JSR-250注解需要设置***@EnableGlobalMethodSecurity(jsr250Enabled=true)***才能使用。

例如:@RolesAllowed({“USER”, “ADMIN”}),代表标注的方法只要具有USER、ADMIN任意一种权限就可以访问。

四、权限异常处理:

  1. AuthenticationEntryPoint:用来解决匿名用户访问无权限资源时的异常

注意:使用AuthenticationEntryPoint会导致原来的/login登录页面失效

  1. AccessDeniedHandler:用来解决认证过的用户访问无权限资源时的异常
    public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response,
                AuthenticationException authException) throws IOException, ServletException {
            response.setCharacterEncoding("utf-8");
            response.setContentType("text/javascript;charset=utf-8");
            response.getWriter().print(JSONObject.toJSONString(RestMsg.error("没有访问权限!")));
        }
    }
    
    @Component
    public class MyAccessDeniedHandler implements AccessDeniedHandler {
        
        @Override
        public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) 
                throws IOException, ServletException {
            response.setStatus(HttpServletResponse.SC_OK);
            response.setContentType("text/html;charset=UTF-8");
            response.getWriter().write(
                    "<html>" +
                            "<body>" +
                            "<div style='width:800px;text-align:center;margin:auto;font-size:24px'>" +
                            "权限不足,请联系管理员" +
                            "</div>" +
                            "</body>" +
                            "</html>"
            );
            response.getWriter().flush();//刷新缓冲区
        }
    }
    
    @Configuration
    @EnableGlobalMethodSecurity(prePostEnabled=true)
    public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private UserDetailsService userDetailsService;
        @Autowired
        private BCryptPasswordEncoder bCryptPasswordEncoder;
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.cors()
                .and()
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/user/sign").permitAll()
            	.anyRequest()
            	.authenticated();
                 
            //添加自定义异常入口
            http.exceptionHandling()
                .authenticationEntryPoint(new CustomAuthenticationEntryPoint())
            	.accessDeniedHandler(new CustomAccessDeineHandler());       
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(userDetailsService)
            	.passwordEncoder(bCryptPasswordEncoder);
        }
    }
    

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

w_t_y_y

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

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

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

打赏作者

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

抵扣说明:

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

余额充值