您好,登錄后才能下訂單哦!
這篇文章主要介紹“SpringBoot怎么整合SpringSecurityOauth2實現鑒權動態權限問題”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“SpringBoot怎么整合SpringSecurityOauth2實現鑒權動態權限問題”文章能幫助大家解決問題。
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
2、不帶token訪問受保護接口http://localhost:7000/admin/user/info
3、登錄后獲取token,帶上token訪問,成功返回了當前的登錄用戶信息
oauth3一共有四種模式,這邊就不做講解了,網上搜一搜,千篇一律
因為現在只考慮做單方應用的,所以使用的是密碼模式。
后面會出一篇SpringCloud+Oauth3的文章,網關鑒權
講一下幾個點吧
1、攔截器配置動態權限
新建一個 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接口時,返回的結果是這樣的
(2)我們規定一下,鑒權失敗的接口返回接口之后,變成下面這種了,是不是更利于我們處理和提示用戶
好了,來看一下是在哪里去配置的吧
我們資源服務器OautyResourceConfig,重寫下下面這部分的代碼,來自定義鑒權異常返回的結果
大伙可以參考下這個http://www.neiyidaogou.com/article/131668.htm
@Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.authenticationEntryPoint(authenticationEntryPoint) //token失效或沒攜帶token時 .accessDeniedHandler(requestAccessDeniedHandler); //權限不足時 }
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; }
那么用戶登錄成功后,如何去拿到用戶的角色集合等呢,這里面就要實現UserDetailsService接口了
@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); } }
然后在我們的安全配置類中設置UserDetailsService為上面的我們自己寫的就行
@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實現鑒權動態權限問題”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。