Bootstrap

shiro加sso登录分配权限菜单

1.前端页面登录,根据路径进入后端执行方法

在这里插入图片描述

2.到后端会先通过gaeway代理找到指定的服务器,通过config后,进入方法

在这里插入图片描述

3.然后进入判断是用户还是会员,然后进入对应的类执行方法

根据注解@Qualifier(“userAuthService”)里面的名称判断是用户还是会员

描述:
1.进入目标类后通过自定义的CustomUsernamePasswordToken类 再次验证用户还是会员并且收集相应的信息,
2.再通过SecurityUtils.getSubject()工具获取主体信息,也就是用户信息,
3.下面根据subject.login(customUsernamePasswordToken)登陆进入相应realm类认证方法里面,在认证方法里面会收集用户名,用于判断这个用户是否存在,成功后将查出整个用户的所有信息,然后再存到user实体类里面,最后加密用户信息和密码返回
4.认证成功后通过subject.getPrincipal()可以直接获取登陆用户存在实体类里面的所有信息
5.生成token,这个token其实就是一个可在整个后端存在的一个类似session一样的信息,这边是先通过用户名+随机字符串设置一个可随机变化的键,也就是可以根据这个值通过redisTemplate.opsForValue().get(accessToken)查询查token
里面的值,也就是用户信息,但是查之前要注入private RedisTemplate redisTemplate;模板,设置底层存储为字符串序列化redisTemplate.setKeySerializer(new StringRedisSerializer());才能获取token里面的用户实体类信息
6.设置底层存储为字符串序列化redisTemplate.setKeySerializer(new StringRedisSerializer());
7.存储新登录的token前,要删除以前的token,先找到所有的key键,Set keys = redisTemplate.keys(userName + “*”);
然后for循环根据键名删除所有的信息。
8.以accessToken为键以user实体类信息为值存入到redis中
9.使用获取的IP和生成的token进行加密运算,
Sha512Hash sha512Hash = new Sha512Hash(remoteIp,accessToken,10);
以accessToken+"CheckToken"为键,以加密后得到的sha512Hash为值存入redsi中
10.最后以封装工具类Result,将accessToken,也就是token的键名返回,方便根据这个键名去查用户信息,主要用作权限

在这里插入图片描述
4.登陆成功,前端获取token键值,然后把值存入session,因为每个前端的方法都要带上这个键值才可以访问后台方法sessionStorage.setItem(“token”,response.data.data);,然后进入主页面去查询菜单
在这里插入图片描述

5.前端携带accesToken到后台,先经过getway这个代理找到对应方法

在这里插入图片描述

6.进入service层执行真正的业务,前面controller不过是代理类

描述:
1.先注入redis模板@Resource
private RedisTemplate redisTemplate;
2. 通过设置底层存储为字符串序列化redisTemplate.setKeySerializer(new StringRedisSerializer());,获取全局通用的token,根据键值获得实体类信息对象User o = (User)redisTemplate.opsForValue().get(accessToken);
3. 根据用户id执行查询菜单的sql获取所有菜单,封装到list里面
4. 定义一个返回的结果集,用于存放数据,通过递归按级别把数据放入结果集

在这里插入图片描述

7.查询所有菜单级别的递归方法

描述:
1.先查出为0的一个节点
2.遍历所有的菜单,用每个值父id和这个值对比,循环结束,将所有父id和改该值对应的数据放到一个集合中,并存入这个父节点里面,直到所有父为0的查完
3.循环明显是二级菜单

 private void findAndBindChild(TreeNode currentTreeNode, List<TreeNode> treeNodeList){
        //再次循环查出的所有menu集合
        for (TreeNode treeNode : treeNodeList) {
            //判断循环节点的父节点id是否和当前节点的id相等,如果相等,说明当前循环节点是当前节点的孩子
            if(currentTreeNode.getId()==treeNode.getParentId()){
                //是孩子,就要添加到当前节点的孩子集合
                //1,获取当前节点的孩子集合
                List<TreeNode> children = currentTreeNode.getChildren();
                //2,一定要判断孩子集合是否为空,如果是第一个孩子,孩子集合肯定为空,如果使用肯定空指针
                if(children==null){
                    //3,如果为空,一定要实例化
                    children = new ArrayList<>();
                }
                // 4,根据上面判断,循环节点的父ID等于当前节点ID说明是孩子,加入
                children.add(treeNode);
                // 5,重新设置孩子集合
                currentTreeNode.setChildren(children);
                // 为了提高效率,每找到一个孩子,就从集合中删除,再次查找集合会变小,效率会变高 理论上可行,但是ArrayList不允许一边遍历一边删除元素
                // treeNodeList.remove(treeNode);
                //6,递归自己调用自己,再次判断当前循环有没有孩子,如果有执行相同操作
                findAndBindChild(treeNode,treeNodeList);
            }
        }
    }

8.shiro用到的几个类

1.判断是单realm还是双realm
配合realm里面的自定义认证器使用

public class CustomModularRealmAuthenticator extends ModularRealmAuthenticator {

    @Override
    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 判断getRealms()是否返回为空
        assertRealmsConfigured();
        // 强制转换回自定义的CustomizedToken  多态  向下转型
        CustomUsernamePasswordToken customizedToken = (CustomUsernamePasswordToken) authenticationToken;
        // 登录类型
        String loginType = customizedToken.getLoginType();
        // 所有Realm
        Collection<Realm> realms = getRealms();
        System.out.println(realms+"..realm对象..");
        // 登录类型对应的所有Realm
        Collection<Realm> typeRealms = new ArrayList<>();
        for (Realm realm : realms) {
            if (realm.getName().contains(loginType)){
                typeRealms.add(realm);
            }
        }
        // 判断是单Realm还是多Realm
        if (typeRealms.size() == 1){
            //单realm认证
            return doSingleRealmAuthentication(typeRealms.iterator().next(), customizedToken);
        }
        else{
            //多realm认证
            return doMultiRealmAuthentication(typeRealms, customizedToken);
        }
    }

2.区分用户还是会员,单双realm认证和登陆前收集信息和验证都有用到

public class CustomUsernamePasswordToken extends UsernamePasswordToken {

    private String loginType;

    /**
     * 自定CustomUsernamePasswordToken 区分是用户还是会员
     * @param username
     * @param password
     * @param loginType
     */
    public CustomUsernamePasswordToken(String username, String password, String loginType) {
        super(username, password);
        this.loginType = loginType;
    }

    public String getLoginType() {
        return loginType;
    }

    public void setLoginType(String loginType) {
        this.loginType = loginType;
    }
}
;