SpringBoot整合SpringSecurityOauth2實(shí)現(xiàn)鑒權(quán)動態(tài)權(quán)限問題
寫在前面
思考:為什么需要鑒權(quán)呢?
系統(tǒng)開發(fā)好上線后,API接口會暴露在互聯(lián)網(wǎng)上會存在一定的安全風(fēng)險(xiǎn),例如:爬蟲、惡意訪問等。因此,我們需要對非開放API接口進(jìn)行用戶鑒權(quán),鑒權(quán)通過之后再允許調(diào)用。
準(zhǔn)備
spring-boot:2.1.4.RELEASE
spring-security-oauth2:2.3.3.RELEASE(如果要使用源碼,不要隨意改動這個(gè)版本號,因?yàn)?.4往上的寫法不一樣了)
mysql:5.7
效果展示
這邊只用了postman做測試,暫時(shí)未使用前端頁面來對接,下個(gè)版本角色菜單權(quán)限分配的會有頁面的展示
1、訪問開放接口http://localhost:7000/open/hello

2、不帶token訪問受保護(hù)接口http://localhost:7000/admin/user/info

3、登錄后獲取token,帶上token訪問,成功返回了當(dāng)前的登錄用戶信息


實(shí)現(xiàn)
oauth2一共有四種模式,這邊就不做講解了,網(wǎng)上搜一搜,千篇一律
因?yàn)楝F(xiàn)在只考慮做單方應(yīng)用的,所以使用的是密碼模式。
后面會出一篇SpringCloud+Oauth2的文章,網(wǎng)關(guān)鑒權(quán)
講一下幾個(gè)點(diǎn)吧
1、攔截器配置動態(tài)權(quán)限

新建一個(gè) MySecurityFilter類,繼承AbstractSecurityInterceptor,并實(shí)現(xiàn)Filter接口
初始化,自定義訪問決策管理器
@PostConstruct
public void init(){
super.setAuthenticationManager(authenticationManager);
super.setAccessDecisionManager(myAccessDecisionManager);
} 自定義 過濾器調(diào)用安全元數(shù)據(jù)源
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.mySecurityMetadataSource;
}先來看一下自定義過濾器調(diào)用安全元數(shù)據(jù)源的核心代碼
以下代碼是用來獲取到當(dāng)前請求進(jìn)來所需要的權(quán)限(角色)
/**
* 獲得當(dāng)前請求所需要的角色
* @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<>(); //無需權(quán)限的,直接返回
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){ //默認(rèn)權(quán)限的則只要登錄了,無需權(quán)限匹配都可訪問
roleNames = new HashSet();
break;
}
Map map = new HashMap();
map.put("resc_id", resc.getResc_id());
// 獲取能訪問該資源的所有權(quán)限(角色)
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("【所需的權(quán)限(角色)】:" + configAttributes);
return configAttributes;
}再來看一下自定義訪問決策管理器核心代碼,這段代碼主要是判斷當(dāng)前登錄用戶(當(dāng)前登錄用戶所擁有的角色會在最后一項(xiàng)寫到)是否擁有該權(quán)限角色
@Override
public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
if(configAttributes == null){ //屬于白名單的,不需要權(quán)限
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())){ //有權(quán)限,可訪問
return;
}
}
}
throw new AccessDeniedException("沒有權(quán)限訪問");
}2、自定義鑒權(quán)異常返回通用結(jié)果
為什么需要這個(gè)呢,如果不配置這個(gè),對于前端,后端來說都很難去理解鑒權(quán)失敗返回的內(nèi)容,還不能統(tǒng)一解讀,廢話不多說,先看看不配置和配置了的返回情況
(1)未自定義前,沒有攜帶token去訪問受保護(hù)的API接口時(shí),返回的結(jié)果是這樣的

(2)我們規(guī)定一下,鑒權(quán)失敗的接口返回接口之后,變成下面這種了,是不是更利于我們處理和提示用戶

好了,來看一下是在哪里去配置的吧
我們資源服務(wù)器OautyResourceConfig,重寫下下面這部分的代碼,來自定義鑒權(quán)異常返回的結(jié)果
大伙可以參考下這個(gè)http://chabaoo.cn/article/131668.htm
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.authenticationEntryPoint(authenticationEntryPoint) //token失效或沒攜帶token時(shí)
.accessDeniedHandler(requestAccessDeniedHandler); //權(quán)限不足時(shí)
}
3、獲取當(dāng)前登錄用戶
第一種:使用JWT攜帶用戶信息,拿到token后再解析
暫不做解釋
第二種:寫一個(gè)SecurityUser實(shí)現(xiàn)UserDetails接口(這個(gè)工程中使用的是這一種)
原來的只有UserDetails接口只有username和password,這里我們加上我們系統(tǒng)中的User
protected User user;
public SecurityUser(User user) {
this.user = user;
}
public User getUser() {
return user;
}
在BaseController,每個(gè)Controller都會繼承這個(gè)的,在里面寫給getUser()的方法,只要用戶帶了token來訪問,我們可以直接獲取當(dāng)前登錄用戶的信息了
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;
}那么用戶登錄成功后,如何去拿到用戶的角色集合等呢,這里面就要實(shí)現(xiàn)UserDetailsService接口了

@Service
public class TokenUserDetailsService implements UserDetailsService{
@Autowired
private LoginService loginService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = loginService.loadUserByUsername(username); //這個(gè)我們拎出來處理
if(Objects.isNull(user))
throw new UsernameNotFoundException("用戶名不存在");
return new SecurityUser(user);
}
}然后在我們的安全配置類中設(shè)置UserDetailsService為上面的我們自己寫的就行

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}最后我們只需要在loginService里面實(shí)現(xiàn)我們的方法就好,根據(jù)我們的實(shí)際業(yè)務(wù)處理判斷該用戶是否存在等
@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));
//權(quán)限集合
Collection<? extends GrantedAuthority> authorities = merge(userRoles);
user.setAuthorities(authorities);
return user;
}
return null;
}大功告成啦,趕緊動起手來吧!
附上源碼地址:https://gitee.com/jae_1995/spring-boot-oauth2
數(shù)據(jù)庫文件在這

到此這篇關(guān)于SpringBoot整合SpringSecurityOauth2實(shí)現(xiàn)鑒權(quán)-動態(tài)權(quán)限的文章就介紹到這了,更多相關(guān)SpringBoot整合SpringSecurityOauth2內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Springboot整合SpringSecurity實(shí)現(xiàn)登錄認(rèn)證和鑒權(quán)全過程
- SpringBoot整合SpringSecurity和JWT和Redis實(shí)現(xiàn)統(tǒng)一鑒權(quán)認(rèn)證
- SpringBoot集成SpringSecurity和JWT做登陸鑒權(quán)的實(shí)現(xiàn)
- SpringSecurity動態(tài)加載用戶角色權(quán)限實(shí)現(xiàn)登錄及鑒權(quán)功能
- springboot+jwt+springSecurity微信小程序授權(quán)登錄問題
- SpringSecurity實(shí)現(xiàn)權(quán)限認(rèn)證與授權(quán)的使用示例
- SpringSecurity進(jìn)行認(rèn)證與授權(quán)的示例代碼
- springSecurity用戶認(rèn)證和授權(quán)的實(shí)現(xiàn)
- 深入淺析springsecurity入門登錄授權(quán)
- mall整合SpringSecurity及JWT實(shí)現(xiàn)認(rèn)證授權(quán)實(shí)戰(zhàn)
- SpringSecurity頁面授權(quán)與登錄驗(yàn)證實(shí)現(xiàn)(內(nèi)存取值與數(shù)據(jù)庫取值)
- SpringSecurity 鑒權(quán)與授權(quán)的具體使用
相關(guān)文章
SpringBoot中使用JWT的實(shí)戰(zhàn)
本文主要介紹了SpringBoot中使用JWT的實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
Java實(shí)現(xiàn)多數(shù)據(jù)源的幾種方式總結(jié)
這篇文章主要給大家總結(jié)介紹了關(guān)于Java實(shí)現(xiàn)多數(shù)據(jù)源的幾種方式,最近項(xiàng)目中的工作流需要查詢多個(gè)數(shù)據(jù)源的數(shù)據(jù),數(shù)據(jù)源可能是不同種類的,需要的朋友可以參考下2023-08-08
Java?Stream函數(shù)式編程管道流結(jié)果處理
這篇文章主要為大家介紹了Java?Stream函數(shù)式編程管道流結(jié)果處理的示例過程解析需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03
詳解Spring Batch 輕量級批處理框架實(shí)踐
這篇文章主要介紹了詳解Spring Batch 輕量級批處理框架實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06
springcloud如何使用dubbo開發(fā)rpc服務(wù)及調(diào)用
這篇文章主要介紹了springcloud如何使用dubbo開發(fā)rpc服務(wù)及調(diào)用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01
SpringMVC中使用bean來接收form表單提交的參數(shù)時(shí)的注意點(diǎn)
本篇文章主要介紹了SpringMVC中使用bean來接收form表單提交的參數(shù)時(shí)的注意點(diǎn),具有很好的參考價(jià)值。下面跟著小編一起來看下吧2017-05-05
詳解java中的PropertyChangeSupport與PropertyChangeListener
這篇文章主要介紹了詳解java中的PropertyChangeSupport與PropertyChangeListener的相關(guān)資料,需要的朋友可以參考下2017-09-09

