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.总结:
更新当前登录用户的权限列表 ,登出后将释放 ,当再次从数据库获取权限数据时将还原【包括自动登录】 ,因此如果需要持久性的更改权限,
还需要修改数据库信息