SpringSecurity5(8-权限注解)

铁佛  金牌会员 | 2025-3-21 07:55:51 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 977|帖子 977|积分 2931

注解使用

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

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

角色校验,请求到来访问控制单位方法时必须包罗 XX 角色才华访问
注意:

  • 角色必须添加 ROLE_前缀
  • 如果要求只有同时拥有 admin 和 user 的用户才华访问某个方法时,@Secured 就无能为力了
  1. @Component
  2. public class UserDetailServiceImpl implements UserDetailsService {
  3.     @Resource
  4.     private PasswordEncoder passwordEncoder;
  5.     @Override
  6.     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  7.         if (username.equals("root")) {
  8.             return new User(username, passwordEncoder.encode("123"), AuthorityUtils.createAuthorityList("ROLE_read"));
  9.         } else if (username.equals("user")) {
  10.             return new User(username, passwordEncoder.encode("123"), AuthorityUtils.createAuthorityList("ROLE_write"));
  11.         }
  12.         return new User(username, passwordEncoder.encode("123"), AuthorityUtils.createAuthorityList("read"));
  13.     }
  14. }
复制代码
  1. @RestController
  2. public class HelloController {
  3.     @RequestMapping("/read")
  4.     @Secured(value = {"ROLE_read"})
  5.     public String read() {
  6.         return "read";
  7.     }
  8.     @RequestMapping("/write")
  9.     @Secured(value = {"ROLE_write"})
  10.     public String write() {
  11.         return "write";
  12.     }
  13.     // 错误实例
  14.     @RequestMapping("/read2")
  15.     @Secured(value = {"read"})
  16.     public String read2() {
  17.         return "read2";
  18.     }
  19. }
复制代码
@PreAuthorize

权限校验,请求到来访问控制单位之前必须包罗 XX 权限才华访问,控制单位方法执行进步行角色校验
  1. @RestController
  2. public class HelloController {
  3.     @RequestMapping("/read3")
  4.     @PreAuthorize(value = "hasRole('ROLE_read')")
  5.     public String read3() {
  6.         return "read3";
  7.     }
  8.     @RequestMapping("/read4")
  9.     @PreAuthorize(value = "hasAnyRole('ROLE_read','ROLE_write')")
  10.     public String read4() {
  11.         return "read4";
  12.     }
  13.     @RequestMapping("/read5")
  14.     @PreAuthorize(value = "hasAnyAuthority('ROLE_read','read')")
  15.     public String read5() {
  16.         return "read5";
  17.     }
  18. }
复制代码
hasRole 与 hasAuthority 的区别

  • hasRole 的值会添加 ROLE_开头进行判断,而 hasAuthority 不会
  • 其他方法判断一致
  1. public abstract class SecurityExpressionRoot implements SecurityExpressionOperations {
  2.     protected final Authentication authentication;
  3.     private AuthenticationTrustResolver trustResolver;
  4.     private RoleHierarchy roleHierarchy;
  5.     private Set<String> roles;
  6.     private String defaultRolePrefix = "ROLE_";
  7.     /** Allows "permitAll" expression */
  8.     public final boolean permitAll = true;
  9.     /** Allows "denyAll" expression */
  10.     public final boolean denyAll = false;
  11.     private PermissionEvaluator permissionEvaluator;
  12.     public final String read = "read";
  13.     public final String write = "write";
  14.     public final String create = "create";
  15.     public final String delete = "delete";
  16.     public final String admin = "administration";
  17.     //...
  18.     public final boolean hasAuthority(String authority) {
  19.         return hasAnyAuthority(authority);
  20.     }
  21.     public final boolean hasAnyAuthority(String... authorities) {
  22.         return hasAnyAuthorityName(null, authorities);
  23.     }
  24.     public final boolean hasRole(String role) {
  25.         return hasAnyRole(role);
  26.     }
  27.     public final boolean hasAnyRole(String... roles) {
  28.         return hasAnyAuthorityName(defaultRolePrefix, roles);
  29.     }
  30.     private boolean hasAnyAuthorityName(String prefix, String... roles) {
  31.         Set<String> roleSet = getAuthoritySet();
  32.         for (String role : roles) {
  33.             String defaultedRole = getRoleWithDefaultPrefix(prefix, role);
  34.             if (roleSet.contains(defaultedRole)) {
  35.                 return true;
  36.             }
  37.         }
  38.         return false;
  39.     }
  40.     // ....
  41.     private static String getRoleWithDefaultPrefix(String defaultRolePrefix, String role) {
  42.         if (role == null) {
  43.             return role;
  44.         }
  45.         if (defaultRolePrefix == null || defaultRolePrefix.length() == 0) {
  46.             return role;
  47.         }
  48.         // 判断是否以 ROLE_开头
  49.         if (role.startsWith(defaultRolePrefix)) {
  50.             return role;
  51.         }
  52.         return defaultRolePrefix + role;
  53.     }
  54. }
复制代码
@PostAuthorize

权限校验,请求到来访问控制单位之后必须包罗 XX 权限才华访问,在方法执行后进行权限校验,适合验证带有返回值的权限
  1. @PostAuthorize("hasRole('ROLE_管理员')")
  2. @RequestMapping("/toMain")
  3. public String toMain(){
  4.     return "main";
  5. }
  6. @GetMapping("/helloUser")
  7. @PostAuthorize("returnObject!=null && returnObject.username == authentication.name")
  8. public User helloUser() {
  9.     Object pricipal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
  10.     User user;
  11.     if("anonymousUser".equals(pricipal)) {
  12.         user = null;
  13.     }else {
  14.         user = (User) pricipal;
  15.     }
  16.     return user;
  17. }
复制代码
@PreFilter

对传递参数值做过滤
  1. @PostMapping("/preFilter")
  2. @PreAuthorize("hasAnyAuthority('admin','update')") // 注意单引号
  3. @PreFilter("filterObject.id % 2 == 0") // id 为偶数才能请求
  4. public String preFilter(@RequestBody List<User> userLists){
  5.     log.info("=== 进入当前 preFilter ====");
  6.     log.info(userLists.toString());
  7.     return "security test 5  preFilter  需要验证的接口";
  8. }
复制代码
@PostFilter

权限验证通事后,留下指定用户名的数据,对返回数据做过滤
  1. @RequestMapping("/postFilter")
  2. @PreAuthorize("hasAnyAuthority('admin','update')") // 注意单引号
  3. @PostFilter("filterObject.username == 'xiangjiao'") // 针对返回数据做过滤
  4. public List<User> postFilter(){
  5.     log.info("=== 进入当前 postFilter ====");
  6.     List<User> userLists = new ArrayList<>();
  7.     userLists.add(new User(1,"xiangjiao","bunana",1,0));
  8.     userLists.add(new User(2,"xiangjiao2","bunana2",1,0));
  9.     return userLists;
  10. }
复制代码
使用注意


  • 使用@EnableGlobalMethodSecurity 开启注解支持后,用户必须实现 UserDetailsService 方法,使用 auth.inMemoryAuthentication()内存管理用户信息会失效
  • 如果注解要使用 permitAll()、isAnonymous()等方法时,需要在 config 方法中取消.anyRequest().authenticated()的设置,否则会无效。
  1. @Configuration
  2. @EnableGlobalMethodSecurity(prePostEnabled=true)
  3. public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
  4.     @Autowired
  5.     private UserDetailsService userDetailsService;
  6.     @Autowired
  7.     private BCryptPasswordEncoder bCryptPasswordEncoder;
  8.     @Override
  9.     protected void configure(HttpSecurity http) throws Exception {
  10.         http.cors()
  11.             .and()
  12.             .csrf().disable();
  13.         // 下面需注释,否则注解无效,生效的是这里的配置
  14.             /*.authorizeRequests()
  15.                 .anyRequest()
  16.                 .authenticated(); */
  17.     }
  18.     @Override
  19.     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  20.         auth.userDetailsService(userDetailsService)
  21.                 .passwordEncoder(bCryptPasswordEncoder);
  22.     }
  23. }
复制代码
  1. @RestController
  2. public class HelloController {
  3.     @RequestMapping("/read3")
  4.     @PreAuthorize(value = "hasRole('ROLE_read')")
  5.     public String read3() {
  6.         return "read3";
  7.     }
  8.     @RequestMapping("/read4")
  9.     @PreAuthorize(value = "hasAnyRole('ROLE_read','ROLE_write')")
  10.     public String read4() {
  11.         return "read4";
  12.     }
  13.     @RequestMapping("/read5")
  14.     @PreAuthorize(value = "hasAnyAuthority('ROLE_read','read')")
  15.     public String read5() {
  16.         return "read5";
  17.     }
  18.     @RequestMapping("/isAnonymous")
  19.     @PreAuthorize(value = "isAnonymous()")
  20.     public String isAnonymous() {
  21.         return "isAnonymous";
  22.     }
  23. }
复制代码
注解方法


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

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

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

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

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

  • permitAll():允许全部人(可无任何权限)访问
  • denyAll():不允许任何(即使有最大权限)访问。
  • isAnonymous():为可匿名(不登录)访问。
  • isAuthenticated():为身份证认证后访问。
  • isRememberMe():为记住我用户操作访问。
  • isFullyAuthenticated():为非匿名且非记住我用户允许访问
JSR-250 注解

注意:使用 JSR-250 注解需要设置@EnableGlobalMethodSecurity(jsr250Enabled = true)才华使用

  • @DenyAll
  • @PermitAll
  • @RolesAllowed
例如:@RolesAllowed({"USER", "ADMIN"}),代表标注的方法只要具有 USER、ADMIN 任意一种权限就可以访问
使用案例

自界说权限校验
  1. interface TestPermissionEvaluator {
  2.     boolean check(Authentication authentication);
  3. }
  4. @Service("testPermissionEvaluator")
  5. public class TestPermissionEvaluatorImpl implements TestPermissionEvaluator {
  6.     public boolean check(Authentication authentication) {
  7.         System.out.println("进入了自定义的匹配器" + authentication);
  8.         return false;
  9.     }
  10. }
复制代码
  1. @PreAuthorize("@testPermissionEvaluator.check(authentication)")
  2. public String test0() {
  3.         return "说明你有自定义权限";
  4. }
复制代码
权限异常处理

AuthenticationEntryPoint

用来解决匿名用户访问无权限资源时的异常
注意:使用 AuthenticationEntryPoint 会导致原来的/login 登录页面失效
  1. public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
  2.     @Override
  3.     public void commence(HttpServletRequest request, HttpServletResponse response,
  4.             AuthenticationException authException) throws IOException, ServletException {
  5.         response.setCharacterEncoding("utf-8");
  6.         response.setContentType("text/javascript;charset=utf-8");
  7.         response.getWriter().print(JSONObject.toJSONString(RestMsg.error("没有访问权限!")));
  8.     }
  9. }
复制代码
AccessDeniedHandler

用来解决认证过的用户访问无权限资源时的异常
  1. @Component
  2. public class MyAccessDeniedHandler implements AccessDeniedHandler {
  3.    
  4.     @Override
  5.     public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e)
  6.             throws IOException, ServletException {
  7.         response.setStatus(HttpServletResponse.SC_OK);
  8.         response.setContentType("text/html;charset=UTF-8");
  9.         response.getWriter().write(
  10.                 "<html>" +
  11.                         "<body>" +
  12.                         "" +
  13.                         "权限不足,请联系管理员" +
  14.                         "" +
  15.                         "</body>" +
  16.                         "</html>"
  17.         );
  18.         response.getWriter().flush();//刷新缓冲区
  19.     }
  20. }
复制代码
SecurityConfig 配置
  1. @Configuration
  2. @EnableGlobalMethodSecurity(prePostEnabled=true)
  3. public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
  4.     @Autowired
  5.     private UserDetailsService userDetailsService;
  6.     @Autowired
  7.     private BCryptPasswordEncoder bCryptPasswordEncoder;
  8.     @Override
  9.     protected void configure(HttpSecurity http) throws Exception {
  10.         http.cors()
  11.             .and()
  12.             .csrf().disable()
  13.             .authorizeRequests()
  14.             .antMatchers("/user/sign").permitAll()
  15.             .anyRequest()
  16.             .authenticated();
  17.             
  18.         //添加自定义异常入口
  19.         http.exceptionHandling()
  20.             .authenticationEntryPoint(new CustomAuthenticationEntryPoint())
  21.             .accessDeniedHandler(new CustomAccessDeineHandler());      
  22.     }
  23.     @Override
  24.     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  25.         auth.userDetailsService(userDetailsService)
  26.                 .passwordEncoder(bCryptPasswordEncoder);
  27.     }
  28. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

铁佛

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表