1.前言
spring security 可以獲取當前登錄的用戶信息,同時提供了接口 來修改權限列表信息 ,
使用這個方法 ,可以動態的修改當前登錄用戶權限。
那么問題來了。。。
如果我是管理員 ,如何動態地修改用戶的權限?比如vip權限?
按照以前的權限使用方法 ,修改數據庫的權限信息后,當前用戶需要重新登錄,才能從數據庫獲取新的權限信息后再更新當前用戶的權限列表,一般是管理員修改權限后,強制用戶重新登錄,
這樣對用戶很不友好 ,使用spring security 可以直接更新當前用戶的權限 ,其實就是重新注冊權限列表信息。
可是問題又來了。。。
spring security 不是只能修改當前登錄用戶的信息么?那么怎么修改別人的?
有兩個解決方案:
(1)方案一:管理員在數據庫修改用戶權限數據后,檢查該用戶是否已經登錄,未登錄則操作結束,
如果已經登錄,則使用websocket通知用戶前端向后端Ajax發送一個修改當前權限的請求。
(2)方案二:管理員在數據庫修改用戶權限數據,檢查該用戶是否已經登錄,未登錄則操作結束,
如果已經登錄,獲取當前用戶存在內存的session,根據session獲取該用戶的認證信息 ,取出權限列表后對其修改,然后重新注冊權限列表。
2.操作
准備一個配置好的spring boot+ spring security的工程 ,詳細操作這里不演示 ,在我的其他隨筆有詳細記錄
(1)目錄結構
(2)添加方法
源碼

package com.example.security5500.controller; import org.apache.commons.lang3.StringUtils; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import java.security.Principal; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 作為授權類 ,用來動態更新權限 */ @RestController public class AuthorityController { //添加權限 ,參數是需要新增的權限名 @RequestMapping("/addAuth") public Map<String,Object> addAuth(String authName){ Map<String,Object> map = new HashMap<>(); if (StringUtils.isBlank(authName)){ map.put("data","權限名稱不可空,參數名authName"); return map; } try { //======================================================== //這一段僅僅是更新當前登錄用戶的權限列表 ,登出后將釋放 ,當再次從數據庫獲取權限數據時將還原 ,因此如果需要持久性的更改權限, // 還需要修改數據庫信息 ,懶得寫 ,這里就不做修改數據庫演示了 // // 得到當前的認證信息 Authentication auth = SecurityContextHolder.getContext().getAuthentication(); // 生成當前的所有授權 List<GrantedAuthority> updatedAuthorities = new ArrayList<>(auth.getAuthorities()); // 添加 ROLE_VIP 授權 updatedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + authName)); // 生成新的認證信息 Authentication newAuth = new UsernamePasswordAuthenticationToken(auth.getPrincipal(), auth.getCredentials(), updatedAuthorities); // 重置認證信息 SecurityContextHolder.getContext().setAuthentication(newAuth); //======================================================== map.put("data","權限 "+authName+" 添加成功"); }catch (Exception e){ e.printStackTrace(); map.put("data","權限添加失敗"); } return map; } //獲取用戶權限信息 @RequestMapping({"/info"}) @ResponseBody public Object info(@AuthenticationPrincipal Principal principal) { return principal; } /* {"authorities":[{"authority":"admin"},{"authority":"user"}], "details":{"remoteAddress":"0:0:0:0:0:0:0:1","sessionId":"1F57B8E39C5D1DB1F875D57D533DB982"}, "authenticated":true,"principal":{"password":null,"username":"xi","authorities":[{"authority":"admin"}, {"authority":"user"}],"accountNonExpired":true,"accountNonLocked":true, "credentialsNonExpired":true,"enabled":true},"credentials":null,"name":"xi"} */ //http://localhost:5500/addAuth?authName=love1 //http://localhost:5500/info }
(3)攔截位置 ,需要權限 ROLE_love1 才可以訪問
3.測試
啟動工程
(1)訪使用一個 有 權限 ROLE_love1 的賬戶
username = cen
password = 11
訪問網址 http://localhost:5500/home
點擊登錄
顯然可以正常運行
訪問網址 http://localhost:5500/info 查看當前登錄用戶的認證信息 ,是有 ROLE_love1 權限的
(2)訪使用一個 沒有 權限 ROLE_love1 的賬戶
username = yue
password = 11
訪問網址 http://localhost:5500/home
選擇 “記住我” ,可以自動登錄
提示 403 ,
訪問網址 http://localhost:5500/info 查看當前登錄用戶的認證信息 ,是 沒有 ROLE_love1 權限的
現在添加權限
訪問網址 http://localhost:5500/addAuth?authName=love1
再次 訪問網址 http://localhost:5500/info 查看當前登錄用戶的認證信息 ,發現 有 ROLE_love1 權限了
再次 訪問網址 http://localhost:5500/home ,發現訪問成功啦
(2)那么現在問題來了,現在是開啟了自動登錄模式 ,那么關閉瀏覽器后 cookie是否會刪除? 如果不刪除 ,新加的權限是否會刪除?
繼續上面yue的登錄頁面 直接關閉瀏覽器,再訪問 http://localhost:5500/home
發現被攔截進入 登錄頁面了
難道被強制登出了?
不 ,查看cookie ,並沒有被刪除
繼續 訪問 http://localhost:5500/hai ,發現是已經自動登錄了
於是查看 認證信息 ,訪問網址 http://localhost:5500/info
發現 ROLE_love1 權限被清除了
(3)為什么會這樣?
有兩個猜測 :
猜測一:在第一次登錄時 ,沒有權限的 認證信息已經存在cookie ,不再修改,自動登錄不再從數據庫獲取權限信息。
猜測二 : 即便是自動登錄 ,每次登錄的都會從數據庫獲取用戶信息並更新 ,以前登錄時存在內存的信息會在瀏覽器銷毀后釋放。
證明:
我在注冊權限的地方加了指令將添加到注冊列表的權限名打印
源碼

package com.example.security5500.securityConfig; import com.example.security5500.entitis.tables.TUser; import com.example.security5500.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; @Service public class DbUserDetailsService implements UserDetailsService { @Autowired private UserService userService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //根據用戶名查詢用戶信息 TUser tUser = userService.getByUsername(username); if (tUser == null){ throw new UsernameNotFoundException("用戶不存在!"); } //權限設置 // List<GrantedAuthority> simpleGrantedAuthorities = new ArrayList<>(); List<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<>(); String role = tUser.getRole(); //分割權限名稱,如 user,admin String[] roles = role.split(","); System.out.println("注冊該賬戶權限"); for (String r :roles){ System.out.println(r); //添加權限 simpleGrantedAuthorities.add(new SimpleGrantedAuthority(r)); } // simpleGrantedAuthorities.add(new SimpleGrantedAuthority("USER")); /** * 創建一個用於認證的用戶對象並返回,包括:用戶名,密碼,角色 */ //輸入參數 return new org.springframework.security.core.userdetails.User(tUser.getUsername(), tUser.getPsw(), simpleGrantedAuthorities); } }
自動登錄后
控制台打印的結果:
因此 ,猜測二是正確的 。
4.總結:
更新當前登錄用戶的權限列表 ,登出后將釋放 ,當再次從數據庫獲取權限數據時將還原【包括自動登錄】 ,因此如果需要持久性的更改權限,
還需要修改數據庫信息