zoukankan      html  css  js  c++  java
  • spring security 动态 修改当前登录用户的 权限

    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.总结:

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

    还需要修改数据库信息
  • 相关阅读:
    restframework 自定义返回响应格式
    restframework 分页器
    Python设计模式
    Pytest系列
    Pytest系列
    Pytest系列 -pytest-dependency 用例依赖
    restframework jwt登录验证
    restframework 自定义json返回格式
    Axure RP8 注册码
    LVM 移除PV步骤
  • 原文地址:https://www.cnblogs.com/c2g5201314/p/13037147.html
Copyright © 2011-2022 走看看