spring security 动态 修改当前登录用户的 权限

时间:2024-03-04 22:08:22

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


}
View Code

 

 

(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);
    }

}
View Code

 

自动登录后

控制台打印的结果:

 

 

因此 ,猜测二是正确的  。

 

 

4.总结:

更新当前登录用户的权限列表 ,登出后将释放 ,当再次从数据库获取权限数据时将还原【包括自动登录】 ,因此如果需要持久性的更改权限,

还需要修改数据库信息