<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          springboot 簡單集成 spring security(二)權(quán)限控制

          共 7523字,需瀏覽 16分鐘

           ·

          2023-05-16 03:42

          簡介

          上一篇我們實現(xiàn)了springboot簡單的集成了spring security,實現(xiàn)了在用戶想要實現(xiàn)某一操作時需要進(jìn)行登陸認(rèn)證,但是在登陸后用戶能夠進(jìn)行所有想要的操作,這并不是我們想要的,我們需要的是當(dāng)用戶訪問后,用戶能夠操作的是我們能夠控制他能夠操作接口。這里我們就需要進(jìn)行權(quán)限的控制。

          1、內(nèi)存中實現(xiàn)權(quán)限控制

          在上一篇中我們在實現(xiàn)用戶認(rèn)證后就沒有進(jìn)行相關(guān)的操作,這里我們想要在內(nèi)存中實現(xiàn)權(quán)限認(rèn)證。需要在securityConfig中重寫configure(HttpSecurity http)方法,在接口中進(jìn)行設(shè)置。

          configure(HttpSecurity http)

          在當(dāng)前方法中能夠?qū)崿F(xiàn)對接口訪問權(quán)限設(shè)置。

              @Override
          protected void configure(HttpSecurity http) throws Exception {
          http
          .authorizeRequests()
          //設(shè)置所有認(rèn)證后的用戶都能夠訪問所有接口,不需要任何權(quán)限
          .anyRequest().authenticated()
          //設(shè)置不需要登陸認(rèn)證就能夠訪問登陸接口
          .and()
          .formLogin()
          .permitAll()
          // 設(shè)置不需要登陸認(rèn)證就能夠訪問登出接口
          .and()
          .logout()
          .permitAll();
          }

          如果我們想要對接口進(jìn)行權(quán)限控制這里我們就要將

               //.anyRequest().authenticated()

          注釋掉,然后再添加

              
                .
                antMatchers
                (
                ""
                )
                .
                hasAnyAuthority
                (
                ""
                )
                

          然后添加想要進(jìn)行權(quán)限控制的接口和設(shè)置訪問當(dāng)前接口需要的權(quán)限我們這里只寫了/selByPhone接口所以我們的代碼如下

              
                .
                antMatchers
                (
                "/selByPhone"
                )
                
          //設(shè)置訪問當(dāng)前接口需要的權(quán)限
          . hasAnyAuthority ( "ROLE_query_user" )

          這里我們設(shè)置當(dāng)前接口需要的權(quán)限是"ROLE_query_user"。完整的securityConfig的configure(HttpSecurity http)方法

              @Override
          protected void configure(HttpSecurity http) throws Exception {
          http
          .authorizeRequests()
          // .anyRequest().authenticated()
          //配置訪問當(dāng)前接口需要的權(quán)限
          .antMatchers("/selByPhone")
          .hasAnyAuthority("ROLE_query_user")
          .and()
          .formLogin()
          .permitAll()
          .and()
          .logout()
          .permitAll();
          }

          然后再數(shù)據(jù)庫中將用戶的權(quán)限設(shè)置為query_user
          然后訪問selByPhone接口
          !](https://img-blog.csdnimg.cn/88043ebba4ad4e3185f343412f8a1bc0.jpeg)

          首先登陸沒有訪問權(quán)限的用戶
          e314c8b3021b880f8df1eaa42d607fe5.webp

          登陸后我們能夠看到當(dāng)前用戶沒有訪問權(quán)限spring security返回了403頁面。
          b6c49d10f9d5f745be273d63c5f3ca51.webp

          我們登陸有權(quán)限的用戶,我們看到能夠成功登陸bed6d1eed5727015d879f243a13217ed.webp

          當(dāng)然這種設(shè)置權(quán)限的方式并不是我們想要的,我們不能在寫完接口后然后再securityConfig中進(jìn)行配置,每次都進(jìn)行這種配置并不是我們需要的,這時我們希望能夠靈活的配置權(quán)限控制。小編這里基于jdbc進(jìn)行動態(tài)的權(quán)限配置。當(dāng)然并不只這一種方式,比如枚舉類等等。

          2、基于jdbc動態(tài)的權(quán)限控制

          想要實現(xiàn)基于jdbc動態(tài)的權(quán)限控制,我們需要攔截請求獲取到當(dāng)前要訪問的接口名稱,然后再去查詢想要訪問當(dāng)前接口需要的權(quán)限。
          想要實現(xiàn)攔截請求的功能那我們就需要創(chuàng)建一個連接器,這里我們實現(xiàn)Filter完成權(quán)限攔截器。然后實現(xiàn)SecurityMetadataSource(安全元數(shù)據(jù)),完成查詢當(dāng)前需要的權(quán)限的功能,這里面我們使用他的子類FilterInvocationSecurityMetadataSource。然后我們再實現(xiàn)AccessDecisionManager(決策訪問管理器)在這里我們判斷當(dāng)前登陸的用戶是否擁有訪問該接口的權(quán)限。

          權(quán)限攔截器
              /**
          * @title: PermissionInterceptor
          * @Author zzm
          * @Date: 2022/4/14 19:49
          * @Version 1.0
          */

          @Service
          public class CustomizeAbstractSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {

          @Autowired
          private FilterInvocationSecurityMetadataSource securityMetadataSource;

          @Autowired
          public void setMyAccessDecisionManager(CustomizeAccessDecisionManager accessDecisionManager) {
          super.setAccessDecisionManager(accessDecisionManager);
          }

          @Override
          public Class<?> getSecureObjectClass() {
          return FilterInvocation.class;
          }

          @Override
          public SecurityMetadataSource obtainSecurityMetadataSource() {
          return this.securityMetadataSource;
          }

          @Override
          public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
          FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
          invoke(fi);
          }

          public void invoke(FilterInvocation fi) throws IOException, ServletException {
          //fi里面有一個被攔截的url
          //里面調(diào)用MyInvocationSecurityMetadataSource的getAttributes(Object object)這個方法獲取fi對應(yīng)的所有權(quán)限
          //再調(diào)用MyAccessDecisionManager的decide方法來校驗用戶的權(quán)限是否足夠
          InterceptorStatusToken token = super.beforeInvocation(fi);
          try {
          //執(zhí)行下一個攔截器
          fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
          } finally {
          super.afterInvocation(token, null);
          }
          }
          }

          這個攔截器是攔截我們請求的url,將當(dāng)前的url執(zhí)行下一個攔截器

          訪問決策管理器
              /**
          * @title: CustomizeFilterInvocationSecurityMetadataSource
          * @Author zzm
          * @Date: 2022/4/14 19:56
          * @Version 1.0
          */

          @Component
          public class CustomizeFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
          AntPathMatcher antPathMatcher = new AntPathMatcher();
          @Resource
          private PermissionsMapper permissionsMapper;

          @Override
          public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
          //獲取請求地址
          String requestUrl = ((FilterInvocation) o).getRequestUrl();
          //查詢當(dāng)前接口需要的權(quán)限
          List<Role> role = permissionsMapper.findRole(requestUrl);
          if(role == null ||role.size() == 0){
          return null;
          }
          String[] attributes = new String[role.size()];
          for (int i = 0; i < role.size(); i++) {
          attributes[i] = "ROLE_"+role.get(i).getRole();
          }

          return SecurityConfig.createList(attributes);
          }

          @Override
          public Collection<ConfigAttribute> getAllConfigAttributes() {
          return null;
          }

          /**
          * 這里一定到改成true要不然啟動會報錯
          * @param aClass
          * @return
          */

          @Override
          public boolean supports(Class<?> aClass) {
          return true;
          }
          }

          這里supports方法中返回的boolean類型的數(shù)據(jù)要返回true,默認(rèn)是false。如果是false就會報錯(圖片)

          訪問決策管理器
              /**
          * @title: CustomizeAccessDecisionManager
          * @Author zzm
          * @Date: 2022/4/14 20:13
          * @Version 1.0
          */

          @Component
          public class CustomizeAccessDecisionManager implements AccessDecisionManager {
          @Override
          public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
          Iterator<ConfigAttribute> iterator = collection.iterator();
          while (iterator.hasNext()) {
          ConfigAttribute ca = iterator.next();
          //當(dāng)前請求需要的權(quán)限
          String needRole = ca.getAttribute();
          //當(dāng)前用戶所具有的權(quán)限
          Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
          for (GrantedAuthority authority : authorities) {
          if (authority.getAuthority().equals(needRole)) {
          return;
          }
          }
          }
          throw new AccessDeniedException("權(quán)限不足!");
          }

          @Override
          public boolean supports(ConfigAttribute configAttribute) {
          return true;
          }

          @Override
          public boolean supports(Class<?> aClass) {
          return true;
          }
          }

          這里通過比對用戶權(quán)限和當(dāng)前url需要的權(quán)限,判斷當(dāng)前用戶的權(quán)限能夠訪問url。

          配置文件

          寫完攔截器后我們需要在WebSecurityConfig中進(jìn)行配置。

                @Override
          protected void configure(HttpSecurity http) throws Exception {
          http
          .authorizeRequests()
          .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
          @Override
          public <O extends FilterSecurityInterceptor> O postProcess(O o) {
          o.setAccessDecisionManager(accessDecisionManager);//決策管理器
          o.setSecurityMetadataSource(securityMetadataSource);//安全元數(shù)據(jù)源
          return o;
          }
          })
          }

          WebSecurityConfig最終

              /**
          * @title: SecurityConfig
          * @Author zzm
          * @Date: 2022/4/7 21:28
          * @Version 1.0
          */

          @Configuration
          public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

          @Autowired
          private UserService userService;

          //訪問決策管理器
          @Autowired
          CustomizeAccessDecisionManager accessDecisionManager;

          //實現(xiàn)權(quán)限攔截
          @Autowired
          CustomizeFilterInvocationSecurityMetadataSource securityMetadataSource;


          @Autowired
          private CustomizeAbstractSecurityInterceptor securityInterceptor;

          @Bean
          public PasswordEncoder passwordEncoder(){
          //提供加密方式
          return new BCryptPasswordEncoder();
          }

          //配置登陸驗證方式
          @Override
          protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          //指定登陸用我們自己的方法
          auth.userDetailsService(userService)
          .passwordEncoder(passwordEncoder());
          }


          @Override
          protected void configure(HttpSecurity http) throws Exception {
          http
          .authorizeRequests()
          .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
          @Override
          public <O extends FilterSecurityInterceptor> O postProcess(O o) {
          o.setAccessDecisionManager(accessDecisionManager);//決策管理器
          o.setSecurityMetadataSource(securityMetadataSource);//安全元數(shù)據(jù)源
          return o;
          }
          })
          .and()
          .formLogin()
          .permitAll()
          .and()
          .logout()
          .permitAll();
          http.addFilterBefore(securityInterceptor, FilterSecurityInterceptor.class);
          }
          }

          3、結(jié)果

          在數(shù)據(jù)庫中設(shè)置訪問/selByPhone接口的權(quán)限為query_user,用戶賬號為123的權(quán)限為query_user,用戶456的訪問權(quán)限為user。
          當(dāng)?shù)卿涃~戶為456時
          5f4cdd034d2f004978e52a2f5a05473a.webp

          當(dāng)?shù)卿涃~戶為123時
          ddfaec6de4987e1cc8b6cff4d5cf92a0.webp


          瀏覽 85
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产粉嫩小泬18p | 国产国产日韩欧美V∧ | 69人妻人人澡人人爽人人精品 | 国产99视频精品免视看10 | 蜜乳在线视频 |