首頁  >  文章  >  Java  >  SpringBoot怎麼整合SpringSecurityOauth2實現鑑權動態權限問題

SpringBoot怎麼整合SpringSecurityOauth2實現鑑權動態權限問題

王林
王林轉載
2023-05-11 10:28:051454瀏覽

準備

spring-boot:2.1.4.RELEASE

spring-security-oauth3:2.3.3.RELEASE(如果要使用原始碼,不要隨意改動這個版本號,因為2.4往上的寫法不一樣了)

mysql:5.7

效果展示

這邊只用了postman做測試,暫時未使用前端頁面來對接,下個版本角色選單權限指派的會有頁面的展示

1、存取開放介面http://localhost:7000/open/hello 

SpringBoot怎麼整合SpringSecurityOauth2實現鑑權動態權限問題

2 、不帶token訪問受​​保護介面http://localhost:7000/admin/user/info

SpringBoot怎麼整合SpringSecurityOauth2實現鑑權動態權限問題

3、登入後取得token,帶上token訪問,成功返回了目前的登入使用者資訊

SpringBoot怎麼整合SpringSecurityOauth2實現鑑權動態權限問題

SpringBoot怎麼整合SpringSecurityOauth2實現鑑權動態權限問題

#oauth3一共有四種模式,這邊就不做講解了,網上搜一搜,千篇一律

因為現在只考慮做單方應用的,所以使用的是密碼模式。

後面會出一篇SpringCloud Oauth3的文章,網關鑑權

來講幾個點吧

1、攔截器配置動態權限

SpringBoot怎麼整合SpringSecurityOauth2實現鑑權動態權限問題

新建一個MySecurityFilter類,繼承AbstractSecurityInterceptor,並實作Filter介面

初始化,自訂存取決策管理器

@PostConstruct
 public void init(){
        super.setAuthenticationManager(authenticationManager);
        super.setAccessDecisionManager(myAccessDecisionManager);
  }

自訂過濾器呼叫安全元數據來源

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

先來看看自訂篩選器呼叫安全性元資料來源的核心程式碼

#以下程式碼是用來取得到目前請求進來所需的權限(角色)

/**
     * 获得当前请求所需要的角色
     * @param object
     * @return
     * @throws IllegalArgumentException
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        String requestUrl = ((FilterInvocation) object).getRequestUrl();

        if (IS_CHANGE_SECURITY) {
            loadResourceDefine();
        }
        if (requestUrl.indexOf("?") > -1) {
            requestUrl = requestUrl.substring(0, requestUrl.indexOf("?"));
        }
        UrlPathMatcher matcher = new UrlPathMatcher();
        List<Object> list = new ArrayList<>();  //无需权限的,直接返回
        list.add("/oauth/**");
        list.add("/open/**");
        if(matcher.pathsMatchesUrl(list,requestUrl))
            return null;

        Set<String> roleNames = new HashSet();
        for (Resc resc: resources) {
            String rescUrl = resc.getResc_url();
            if (matcher.pathMatchesUrl(rescUrl, requestUrl)) {
                if(resc.getParent_resc_id() != null && resc.getParent_resc_id().intValue() == 1){   //默认权限的则只要登录了,无需权限匹配都可访问
                    roleNames = new HashSet();
                    break;
                }
                Map map = new HashMap();
                map.put("resc_id", resc.getResc_id());
                // 获取能访问该资源的所有权限(角色)
                List<RoleRescDTO> roles = roleRescMapper.findAll(map);
                for (RoleRescDTO rr : roles)
                    roleNames.add(rr.getRole_name());
            }
        }

        Set<ConfigAttribute> configAttributes = new HashSet();
        for(String roleName:roleNames)
            configAttributes.add(new SecurityConfig(roleName));

        log.debug("【所需的权限(角色)】:" + configAttributes);

        return configAttributes;
    }

再來看一下自訂存取決策管理器核心程式碼,這段程式碼主要是判斷目前登入使用者(目前登入使用者所擁有的角色會在最後一項寫到)是否擁有該權限角色

@Override
    public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        if(configAttributes == null){   //属于白名单的,不需要权限
            return;
        }
        Iterator<ConfigAttribute> iterator = configAttributes.iterator();
        while (iterator.hasNext()){
            ConfigAttribute configAttribute = iterator.next();
            String needPermission = configAttribute.getAttribute();
            for (GrantedAuthority ga: authentication.getAuthorities()) {
                if(needPermission.equals(ga.getAuthority())){   //有权限,可访问
                    return;
                }
            }
        }
        throw new AccessDeniedException("没有权限访问");

    }

2、自訂鑑權異常回傳通用結果

為什麼需要這個呢,如果不配置這個,對於前端,後端來說都很難去理解鑑權失敗回傳的內容,還不能統一解讀,廢話不多說,先看看不配置和配置了的返回情況

(1)未自定義前,沒有攜帶token去訪問受保護的API接口時,返回的結果是這樣的

SpringBoot怎麼整合SpringSecurityOauth2實現鑑權動態權限問題

(2)我們規定一下,鑑權失敗的介面回傳介面之後,變成下面這種了,是不是更利於我們處理和提示使用者

SpringBoot怎麼整合SpringSecurityOauth2實現鑑權動態權限問題

好了,來看看哪裡去設定的吧

我們資源伺服器OautyResourceConfig,重寫下下面這部分的程式碼,來自訂鑑權異常回傳的結果

大夥可以參考下這個https://www.yisu.com/article/131668.htm

@Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.authenticationEntryPoint(authenticationEntryPoint)    //token失效或没携带token时
                .accessDeniedHandler(requestAccessDeniedHandler);   //权限不足时
    }

SpringBoot怎麼整合SpringSecurityOauth2實現鑑權動態權限問題

##3、取得當前登入使用者

第一種:使用JWT攜帶使用者訊息,拿到token後再解析

暫時不做解釋

第二種:寫一個SecurityUser實作UserDetails接口(這個工程使用的是這一種)

原來的只有UserDetails介面只有username和password,這裡我們加上我們系統中的User

protected User user;
    public SecurityUser(User user) {
        this.user = user;
    }

    public User getUser() {
        return user;
    }

在BaseController,每個Controller都會繼承這個的,在裡面寫給getUser()的方法,只要用戶帶了token來訪問,我們可以直接獲取當前登錄用戶的信息了

protected User getUser() {
        try {
            SecurityUser userDetails = (SecurityUser) SecurityContextHolder.getContext().getAuthentication()
                    .getPrincipal();

            User user = userDetails.getUser();
            log.debug("【用户:】:" + user);

            return user;
        } catch (Exception e) {
        }
        return null;
    }

那麼用戶登錄成功後,如何去拿到用戶的角色集合等呢,這裡面就要實作UserDetailsS​​ervice介面了

SpringBoot怎麼整合SpringSecurityOauth2實現鑑權動態權限問題

@Service
public class TokenUserDetailsService implements UserDetailsService{

    @Autowired
    private LoginService loginService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = loginService.loadUserByUsername(username);  //这个我们拎出来处理
        if(Objects.isNull(user))
            throw new UsernameNotFoundException("用户名不存在");
        return new SecurityUser(user);
    }
}

然後在我們的安全設定類別中設定UserDetailsS​​ervice為上面的我們自己寫的就行

SpringBoot怎麼整合SpringSecurityOauth2實現鑑權動態權限問題

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

最後我們只需要在loginService裡面實現我們的方法就好,根據我們的實際業務處理判斷該用戶是否存在等

@Override
    public User loadUserByUsername(String username){
        log.debug(username);
        Map map = new HashMap();
        map.put("username",username);
        map.put("is_deleted",-1);
        User user = userMapper.findByUsername(map);
        if(user != null){
            map = new HashMap();
            map.put("user_id",user.getUser_id());
            //查询用户的角色
            List<UserRoleDTO> userRoles = userRoleMapper.findAll(map);
            user.setRoles(listRoles(userRoles));
            //权限集合
            Collection<? extends GrantedAuthority> authorities = merge(userRoles);
            user.setAuthorities(authorities);
            return user;
        }
        return null;

    }

資料庫檔案在這

SpringBoot怎麼整合SpringSecurityOauth2實現鑑權動態權限問題

#

以上是SpringBoot怎麼整合SpringSecurityOauth2實現鑑權動態權限問題的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除