十、Spring Boot集成Spring Security之HTTP请求授权

打印 上一主题 下一主题

主题 1025|帖子 1025|积分 3075

目录

前言

本文介绍HTTP请求授权工作原理、配置及适用场景,共同以下内容观看效果更佳!!!
一、HTTP请求授权工作原理

​        基于Spring Security最新的Http请求授权讲授,不再利用旧版的请求授权

  • 授权过滤器AuthorizationFilter获取认证信息
  • 调用RequestMatcherDelegatingAuthorizationManager的check方法验证该用户是否具有该请求的授权
  • RequestMatcherDelegatingAuthorizationManager根据配置的请求和授权关系校验用户是否具有当前请求的授权并返回授权结果
  • AuthorizationFilter处置惩罚授权结果,授权成功则继续调用过滤器链,否则抛出AccessDeniedException异常
  • 认证失败时,ExceptionTranslationFilter处置惩罚AccessDeniedException异常,如果是当前认证是匿名认证大概RememberMe认证则调用AuthenticationEntryPoint的commence方法,否则调用AccessDeniedHandler的handler方法
  • 工作原理图如下

二、HTTP请求授权配置

1、添加用户权限
  1. package com.yu.demo.spring.impl;
  2. import com.yu.demo.entity.UserDetailsImpl;
  3. import org.springframework.security.core.GrantedAuthority;
  4. import org.springframework.security.core.authority.SimpleGrantedAuthority;
  5. import org.springframework.security.core.userdetails.UserDetails;
  6. import org.springframework.security.core.userdetails.UserDetailsService;
  7. import org.springframework.security.core.userdetails.UsernameNotFoundException;
  8. import org.springframework.stereotype.Service;
  9. import java.util.ArrayList;
  10. import java.util.List;
  11. import java.util.UUID;
  12. @Service
  13. public class UserDetailsServiceImpl implements UserDetailsService {
  14.     //@Autowired
  15.     //private UserService userService;
  16.     // @Autowired
  17.     //private UserRoleService userRoleService;
  18.     @Override
  19.     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  20.         //TODO 通过username从数据库中获取用户,将用户转UserDetails
  21.         //User user = userService.getByUsername(username);
  22.         //TODO 从数据库实现查询权限并转化为List<GrantedAuthority>
  23.         //List<String> roleIds = userRoleService.listRoleIdByUsername(username);
  24.         //List<GrantedAuthority> grantedAuthorities = new ArrayList<>(roleIds.size());
  25.         //roleIds.forEach(roleId -> grantedAuthorities.add(new SimpleGrantedAuthority(roleId)));
  26.         //return new User(username, user.getPassword(), user.getEnable(), user.getAccountNonExpired(), user.getCredentialsNonExpired(), user.getAccountNonLocked(), user.getAuthorities());
  27.         //测试使用,指定权限
  28.         List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
  29.         //与hasXxxRole匹配时添加ROLE_前缀
  30.         grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
  31.         //与hasXxxAuthority匹配时原始值
  32.         grantedAuthorities.add(new SimpleGrantedAuthority("OPERATE"));
  33.         //{noop}不使用密码加密器,密码123的都可以验证成功
  34.         UserDetailsImpl userDetails = new UserDetailsImpl(username, "{noop}123", true, true, true, true, grantedAuthorities);
  35.         //userDetails中设置token,该token只是实现认证流程,未使用jwt
  36.         userDetails.setToken(UUID.randomUUID().toString());
  37.         return userDetails;
  38.     }
  39. }
复制代码
2、配置ExceptionTranslationFilter自定义异常处置惩罚器


  • 因AuthorizationFilter授权失败时会抛出异常,该异常由ExceptionTranslationFilter处置惩罚,以是要配置自定义的异常处置惩罚器。
  • 自定义AccessDeniedHandler和AuthenticationEntryPoint异常处置惩罚器(可以用一个类实现认证授权相关的全部接口,也可以利用多个类分别实现)。
  1. package com.yu.demo.spring.impl;
  2. import com.yu.demo.entity.ApiResp;
  3. import com.yu.demo.entity.UserDetailsImpl;
  4. import com.yu.demo.util.SpringUtil;
  5. import org.springframework.security.access.AccessDeniedException;
  6. import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  7. import org.springframework.security.core.Authentication;
  8. import org.springframework.security.core.AuthenticationException;
  9. import org.springframework.security.core.GrantedAuthority;
  10. import org.springframework.security.core.authority.SimpleGrantedAuthority;
  11. import org.springframework.security.web.AuthenticationEntryPoint;
  12. import org.springframework.security.web.access.AccessDeniedHandler;
  13. import org.springframework.security.web.authentication.AuthenticationFailureHandler;
  14. import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
  15. import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
  16. import org.springframework.stereotype.Component;
  17. import javax.servlet.ServletException;
  18. import javax.servlet.http.HttpServletRequest;
  19. import javax.servlet.http.HttpServletResponse;
  20. import java.io.IOException;
  21. import java.util.HashSet;
  22. import java.util.Set;
  23. @Component
  24. public class LoginResultHandler implements AuthenticationSuccessHandler, LogoutSuccessHandler, AuthenticationEntryPoint, AuthenticationFailureHandler, AccessDeniedHandler {
  25.     /**
  26.      * 登录成功
  27.      */
  28.     @Override
  29.     public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
  30.         UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = (UsernamePasswordAuthenticationToken) authentication;
  31.         UserDetailsImpl userDetailsImpl = (UserDetailsImpl) usernamePasswordAuthenticationToken.getPrincipal();
  32.         //token返回到前端
  33.         SpringUtil.respJson(response, ApiResp.success(userDetailsImpl.getToken()));
  34.     }
  35.     /**
  36.      * 登录失败
  37.      */
  38.     @Override
  39.     public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
  40.         SpringUtil.respJson(response, ApiResp.loginFailure());
  41.     }
  42.     /**
  43.      * 登出成功
  44.      */
  45.     @Override
  46.     public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
  47.         SpringUtil.respJson(response, ApiResp.success());
  48.     }
  49.     /**
  50.      * 未登录调用需要登录的接口时
  51.      */
  52.     @Override
  53.     public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
  54.         SpringUtil.respJson(response, ApiResp.notLogin());
  55.     }
  56.     /**
  57.      * 已登录调用未授权的接口时
  58.      */
  59.     @Override
  60.     public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
  61.         SpringUtil.respJson(response, ApiResp.forbidden());
  62.     }
  63. }
复制代码

  • 配置异常处置惩罚:
  1.                 //异常处理配置
  2.                 .exceptionHandling(exceptionHandlingCustomizer -> exceptionHandlingCustomizer
  3.                         //授权失败处理器(登录账号访问未授权的资源时)
  4.                         .accessDeniedHandler(loginResultHandler)
  5.                         //登录失败处理器(匿账号访问需要未授权的资源时)
  6.                         .authenticationEntryPoint(loginResultHandler))
复制代码
3、HTTP请求授权配置


  • 本文利用最新的authorizeHttpRequests(AuthorizationFilter+AuthorizationManager)配置,不在利用authorizeRequests(FilterSecurityInterceptor+AccessDecisionManager+AccessDecisionVoter)
  • 请求和授权配置成对出现,配置在前的优先级更高
  • 请求种类

    • antMatchers:Ant风格的路径模式,?(匹配一个字符)、*(匹配零个或多个字符,但不包括目录分隔符)、**(匹配零个或多个目录)
    • mvcMatchers:Spring MVC的路径模式,支持路径变量和请求参数
    • regexMatchers:正则表达式路径模式
    • requestMatchers:实现RequestMatcher自定义匹配逻辑
    • anyRequest:未匹配的其他请求,只能有一个且只能放在末了

  • 授权种类

    • permitAll:匿名或登录用户都允许访问
    • denyAll:匿名和登录用户都不允许访问
    • hasAuthority:有配置的权限允许访问,AuthorityAuthorizationManager校验
    • hasRole:有配置的角色允许访问,ROLE_{配置角色}与用户权限匹配,AuthorityAuthorizationManager校验
    • hasAnyAuthority:有配置的恣意一个权限的允许访问,AuthorityAuthorizationManager校验
    • hasAnyRole:有配置的恣意一个角色允许访问,ROLE_{配置角色}与用户权限匹配,AuthorityAuthorizationManager校验
    • authenticated:已认证(不包括匿名)的允许访问,AuthenticatedAuthorizationManager校验
    • access:自定义授权处置惩罚

  • 因authorizeHttpRequests不支持利用anonymous()的方式配置匿名访问,未自定义匿名角色时可以通过hasRole("ANONYMOUS")大概hasAuthority("ROLE_ANONYMOUS")或其他类似的方式实现允许匿名请求的设置


  • http请求授权配置
  1.                 //http请求授权
  2.                 .authorizeHttpRequests(authorizeHttpRequestsCustomizer -> authorizeHttpRequestsCustomizer
  3.                         //不允许访问
  4.                         .antMatchers("/test/deny")
  5.                         .denyAll()
  6.                         //允许匿名访问
  7.                         .antMatchers("/test/anonymous")
  8.                         .hasRole("ANONYMOUS")
  9.                         //允许访问
  10.                         .antMatchers("/test/permit")
  11.                         .permitAll()
  12.                         //测试使用:拥有ADMIN角色
  13.                         .antMatchers("/test/admin")
  14.                         //拥有ROLE_ADMIN权限,配置的角色不能以ROLE_作为前缀
  15.                         .hasRole("ADMIN")
  16.                         //测试使用:拥有OPERATE权限
  17.                         .antMatchers("/test/operate")
  18.                         //拥有OPERATE权限
  19.                         .hasAuthority("OPERATE")
  20.                         //其他的任何请求
  21.                         .anyRequest()
  22.                         //需要认证,且不能是匿名
  23.                         .authenticated())
复制代码

  • 完备过滤器链配置
  1. package com.yu.demo.config;import com.yu.demo.spring.filter.RestfulLoginConfigurer;import com.yu.demo.spring.filter.RestfulUsernamePasswordAuthenticationFilter;import com.yu.demo.spring.impl.LoginResultHandler;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.HttpMethod;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer;import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer;import org.springframework.security.web.SecurityFilterChain;import org.springframework.security.web.context.SecurityContextRepository;@Configuration@EnableWebSecuritypublic class SecurityConfig {    //登录参数用户名    private static final String LOGIN_ARG_USERNAME = "username";    //登录参数密码    private static final String LOGIN_ARG_PASSWORD = "password";    //登录请求类型    private static final String LOGIN_HTTP_METHOD = HttpMethod.POST.name();    //登录请求所在    private static final String LOGIN_URL = "/login";    //登出请求所在    private static final String LOGOUT_URL = "/logout";    @Autowired    private LoginResultHandler loginResultHandler;    @Autowired    private SecurityContextRepository securityContextRepository;    @Bean    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {        httpSecurity                //禁用UsernamePasswordAuthenticationFilter、DefaultLoginPageGeneratingFilter、DefaultLogoutPageGeneratingFilter                .formLogin(FormLoginConfigurer::disable)                //禁用BasicAuthenticationFilter                .httpBasic(HttpBasicConfigurer::disable)                //禁用CsrfFilter                .csrf(CsrfConfigurer::disable)                //禁用SessionManagementFilter                .sessionManagement(SessionManagementConfigurer::disable)                //异常处理配置
  2.                 .exceptionHandling(exceptionHandlingCustomizer -> exceptionHandlingCustomizer
  3.                         //授权失败处理器(登录账号访问未授权的资源时)
  4.                         .accessDeniedHandler(loginResultHandler)
  5.                         //登录失败处理器(匿账号访问需要未授权的资源时)
  6.                         .authenticationEntryPoint(loginResultHandler))                //http请求授权
  7.                 .authorizeHttpRequests(authorizeHttpRequestsCustomizer -> authorizeHttpRequestsCustomizer
  8.                         //不允许访问
  9.                         .antMatchers("/test/deny")
  10.                         .denyAll()
  11.                         //允许匿名访问
  12.                         .antMatchers("/test/anonymous")
  13.                         .hasRole("ANONYMOUS")
  14.                         //允许访问
  15.                         .antMatchers("/test/permit")
  16.                         .permitAll()
  17.                         //测试使用:拥有ADMIN角色
  18.                         .antMatchers("/test/admin")
  19.                         //拥有ROLE_ADMIN权限,配置的角色不能以ROLE_作为前缀
  20.                         .hasRole("ADMIN")
  21.                         //测试使用:拥有OPERATE权限
  22.                         .antMatchers("/test/operate")
  23.                         //拥有OPERATE权限
  24.                         .hasAuthority("OPERATE")
  25.                         //其他的任何请求
  26.                         .anyRequest()
  27.                         //需要认证,且不能是匿名
  28.                         .authenticated())                //安全上下文配置                .securityContext(securityContextCustomizer -> securityContextCustomizer                        //设置自定义securityContext仓库                        .securityContextRepository(securityContextRepository)                        //显示保存SecurityContext,官方保举                        .requireExplicitSave(true))                //登出配置                .logout(logoutCustomizer -> logoutCustomizer                        //登出所在                        .logoutUrl(LOGOUT_URL)                        //登出成功处置惩罚器                        .logoutSuccessHandler(loginResultHandler)                )                //注册自定义登录过滤器的配置器:自动注册自定义登录过滤器;                //须要重写FilterOrderRegistration的构造方法FilterOrderRegistration(){},在构造方法中添加自定义过滤器的序号,否则注册不成功                .apply(new RestfulLoginConfigurer(new RestfulUsernamePasswordAuthenticationFilter(LOGIN_ARG_USERNAME, LOGIN_ARG_PASSWORD, LOGIN_URL, LOGIN_HTTP_METHOD), LOGIN_URL, LOGIN_HTTP_METHOD))                //设置登录所在:未设置时系统默认生成登录页面,登录所在/login                .loginPage(LOGIN_URL)                //设置登录成功之后的处置惩罚器                .successHandler(loginResultHandler)                //设置登录失败之后的处置惩罚器                .failureHandler(loginResultHandler);        //创建过滤器链对象        return httpSecurity.build();    }}
复制代码
三、测试接口

1、测试类
  1. package com.yu.demo.web;
  2. import com.yu.demo.entity.ApiResp;
  3. import org.springframework.web.bind.annotation.GetMapping;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.bind.annotation.RestController;
  6. @RestController
  7. @RequestMapping("/test")
  8. public class TestController {
  9.     @GetMapping("/hello")
  10.     public ApiResp hello() {
  11.         return ApiResp.success("hello");
  12.     }
  13.     /**
  14.      * 匿名允许访问接口地址
  15.      */
  16.     @GetMapping("/anonymous")
  17.     public ApiResp anonymous() {
  18.         return ApiResp.success("anonymous");
  19.     }
  20.     /**
  21.      * 禁止访问接口地址
  22.      */
  23.     @GetMapping("/deny")
  24.     public ApiResp deny() {
  25.         return ApiResp.success("deny");
  26.     }
  27.     /**
  28.      * 允许访问接口地址
  29.      */
  30.     @GetMapping("/permit")
  31.     public ApiResp permit() {
  32.         return ApiResp.success("permit");
  33.     }
  34.     /**
  35.      * 拥有ADMIN角色或ROLE_ADMIN权限允许访问接口地址
  36.      */
  37.     @GetMapping("/admin")
  38.     public ApiResp admin() {
  39.         return ApiResp.success("admin");
  40.     }
  41.     /**
  42.      * 拥有OPERATE权限的允许访问接口地址
  43.      */
  44.     @GetMapping("/operate")
  45.     public ApiResp operate() {
  46.         return ApiResp.success("operate");
  47.     }
  48. }
复制代码
2、测试


  • 登录获取token


  • admin接口测试


  • 其他接口不在逐一测试,有疑问或问题评论或私聊
四、总结


  • 授权是拿用户的权限和可以访问接口的权限进行匹配,匹配成功时授权成功,匹配失败时授权失败
  • 用户的权限对象是SimpleGrantedAuthority,字符串属性role
  • 接口的role权限会通过ROLE_{role}转化为SimpleGrantedAuthority及其字符串属性role
  • 接口的authority权限会直接转化为SimpleGrantedAuthority及其字符串属性role
  • 拥有ROLE_ANONYMOUS权限大概ANONYMOUS角色可以访问匿名接口
  • 后续会讲利用HTTP请求授权+自定义AuthorizationManager方式实现基于RBAC权限模型,接待连续关注
  • 源码下载

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

本帖子中包含更多资源

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

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

南飓风

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表