欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

动吧旅游生态系统——用户管理模块——shiro权限认证分析

程序员文章站 2022-04-15 18:57:05
访问网页需要验证登录、不同的用户访问同一资源有不同的权限等都可以通过shiro实现认证。1.shiro介绍2.业务分析2.1 访问资源流程anon、authc等是一些默认的过滤器,而这些过滤器由shiroFilterFactory工厂提供,而该工厂可以由shiroFilterFactoryBean对象创建,所以在配置类中需要配置该bean对象。过滤器在servlet之前执行,而springmvc集成了servlet,即在controller前对资源进行了拦截,拦截后会调用security....

访问网页需要验证登录、不同的用户访问同一资源有不同的权限等都可以通过shiro实现认证。

1.shiro介绍

动吧旅游生态系统——用户管理模块——shiro权限认证分析
动吧旅游生态系统——用户管理模块——shiro权限认证分析

2.业务分析

2.1 访问资源流程

anon、authc等是一些默认的过滤器,而这些过滤器由shiroFilterFactory工厂提供,而该工厂可以由shiroFilterFactoryBean对象创建,所以在配置类中需要配置该bean对象。
过滤器在servlet之前执行,而springmvc集成了servlet,即在controller前对资源进行了拦截,拦截后会调用securityManager进行认证,该对象定义了一系列的认证授权方法,一旦遇到需要认证的请求就跳回登录页面
shiroFilterFactoryBean对象规定了哪些资源需要认证访问,哪些可以直接访问,而具体有没有认证需要交给securityManager去检测该请求有没有被认证
动吧旅游生态系统——用户管理模块——shiro权限认证分析
动吧旅游生态系统——用户管理模块——shiro权限认证分析

2.2 登录验证

登陆界面输入的用户信息controller可以拿到,通过subject传给securutyManager,Realm中可以获取用户信息
动吧旅游生态系统——用户管理模块——shiro权限认证分析
动吧旅游生态系统——用户管理模块——shiro权限认证分析

放行登录的contrller
调用doLogin后 security Manager会将token交给认证管理器,认证管理器再交给realm做具体认证
动吧旅游生态系统——用户管理模块——shiro权限认证分析
动吧旅游生态系统——用户管理模块——shiro权限认证分析

2.3 授权分析

Shiro授权基于spring原生aop实现
Security Manager交给权限管理器去进行授权
动吧旅游生态系统——用户管理模块——shiro权限认证分析
动吧旅游生态系统——用户管理模块——shiro权限认证分析
动吧旅游生态系统——用户管理模块——shiro权限认证分析

2.4使用缓存

当我们进行授权操作时,每次都会从数据库查询用户权限信息,为了提高授权性能,可以将用户权限信息查询出来以后进行缓存,下次授权时从缓存取数据即可。
CacheManager是管理cache的一个对象,底层是用的softHashmap()对象,软引用,表示内存不足,如果GC触发,容器中关联的对象可以被回收。
动吧旅游生态系统——用户管理模块——shiro权限认证分析

2.5shiro记住我

使用session Manager和cookie Manager
过滤器改成user,表明可以从浏览器的cookie获取用户信息,
设置cookie的超时时间,不设置默认是会话cookie,浏览器关闭即结束,再打开浏览器cookie无效。动吧旅游生态系统——用户管理模块——shiro权限认证分析
动吧旅游生态系统——用户管理模块——shiro权限认证分析
动吧旅游生态系统——用户管理模块——shiro权限认证分析

添加url重写,避免客户端将cookie禁用,无法通过cookie记录该客户端信息,通过重写的url后缀可以识别是同一客户端。
使用shiro框架实现认证操作,用户登录成功会将用户信息写入到会话对象中,其默认时长为30分钟,session的超时时间从对该页面不做任何操作开始计算,到达时间相当于锁屏,会清空session,也就表明需要重新登录
动吧旅游生态系统——用户管理模块——shiro权限认证分析

3.代码呈现

UserController中调用登录方法,subject主体携带用户信息的token,交给security Manager,security Manager交给认证管理器,认证管理器再交给realm去认证,ream中token包含了用户信息

 @RequestMapping("doLogin")
    public JsonResult doLogin(String username,String password,boolean isRememberMe){
        //1.获取Subject对象
        Subject subject = SecurityUtils.getSubject();
        //2.通过Subject提交用户信息,交给shiro框架进行认证操作
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        token.setRememberMe(isRememberMe);
        subject.login(token);
        //1)token会传给shiro的SecurityManager
        //2)SecurityManager将token传递给认证管理器
        //3)认证管理器会将token传递给realm
        return new JsonResult("login ok");
    }

配置类 分析可知:需要security Manager、工厂对象

package com.cy.pj.common.config;


import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;

@Configuration//配置类——可以直接写在启动类中,交给spring管理的一个配置对象
public class SpringShiroConfig {

    @Bean//此注解描述的方法的返回值交给spring管理
    public SecurityManager securityManager(Realm realm,CacheManager cacheManager,
                                           RememberMeManager rememberMeManager,
                                           SessionManager sessionManager){
        DefaultWebSecurityManager sManager= new DefaultWebSecurityManager();
        sManager.setRealm(realm);
        sManager.setCacheManager(cacheManager);
        sManager.setRememberMeManager(rememberMeManager);
        sManager.setSessionManager(sessionManager);
        return sManager;
    }
    @Bean//设置一些访问规则——匿名访问的资源,认证的访问资源
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        ShiroFilterFactoryBean sFBean = new ShiroFilterFactoryBean();
        sFBean.setSecurityManager(securityManager);//这个请求是否已认证交给securityManager去做
        sFBean.setLoginUrl("/doLoginUI");//放行登录页面
        sFBean.setSuccessUrl("/doIndexUI");
        //定义map指定请求过滤规则(哪些资源允许匿名访问,哪些必须认证访问)
        LinkedHashMap<String,String> map= new LinkedHashMap<>();
        //静态资源允许匿名访问:"anon"
        map.put("/bower_components/**","anon");
        map.put("/build/**","anon");
        map.put("/dist/**","anon");
        map.put("/plugins/**","anon");
        map.put("/user/doLogin","anon");
        map.put("/doLogout","logout");
        //除了匿名访问的资源,其它都要认证("authc")后访问
//        map.put("/**","authc");
        map.put("/**","user");//记住我
        sFBean.setFilterChainDefinitionMap(map);
        return sFBean;
    }

    @Bean
    public CacheManager shiroCacheManager(){
        //具体使用的是softHashmap,软引用cache,好处在于内存不够时淘汰缓存中的数据
        return new MemoryConstrainedCacheManager();
    }
    @Bean
    public RememberMeManager cookieRememberMeManager(){
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setMaxAge(7*24*60*60);//7//不设置时间相当于临时cookie,浏览器关闭cookie生命周期结束,设置生命周期,关闭浏览器cookie信息还存在
        cookieRememberMeManager.setCookie(cookie);
        return cookieRememberMeManager;
    }

    @Bean
    public SessionManager sessionManager() {
        DefaultWebSessionManager sManager= new DefaultWebSessionManager();
        sManager.setGlobalSessionTimeout(60*60*1000);//60分钟
        sManager.setSessionIdUrlRewritingEnabled(false);//不启用url重写,
        return sManager;
    }

}

realm相当于service实现类,去做具体的认证和授权

package com.cy.pj.sys.service.realm;


import com.cy.pj.sys.dao.SysMenuDao;
import com.cy.pj.sys.dao.SysRoleMenuDao;
import com.cy.pj.sys.dao.SysUserDao;
import com.cy.pj.sys.dao.SysUserRoleDao;
import com.cy.pj.sys.pojo.SysUser;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Service
public class ShiroUserRealm extends AuthorizingRealm {
    @Autowired
    private SysUserDao sysUserDao;
    @Autowired
    private SysMenuDao sysMenuDao;
    @Autowired
    private SysUserRoleDao sysUserRoleDao;
    @Autowired
    private SysRoleMenuDao sysRoleMenuDao;

    @Override//重写get或set方法都可以——凭证匹配器,返回密码匹配对象——解密方式
    public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
        //构建凭证匹配对象
        HashedCredentialsMatcher cMatcher= new HashedCredentialsMatcher();
        //设置加密算法
        cMatcher.setHashAlgorithmName("MD5");
        //设置加密次数
        cMatcher.setHashIterations(1);
        super.setCredentialsMatcher(cMatcher);
    }

      /*  @Override
    public CredentialsMatcher getCredentialsMatcher(){
        HashedCredentialsMatcher cMatcher = new HashedCredentialsMatcher();
        cMatcher.setHashAlgorithmName("MD5");
        cMatcher.setHashIterations(1);
       return cMatcher;
    }*/



    @Override
    //权限认证——查询角色——查询对应的菜单   ——用aop,在方法上自定义注解,根据方法上的注解和用户拥有的权限是否一致
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SysUser user=(SysUser)principals.getPrimaryPrincipal();//获取用户身份,和登录时封装的对应
        Integer userId = user.getId();
        //基于用户Id查询对应角色
        List<Integer> roleIds = sysUserRoleDao.findRoleIdsByUserId(userId);
        if(roleIds==null||roleIds.size()==0)
            throw new AuthorizationException();
        //基于角色Ids查询对应菜单
        List<Integer> menuIds = sysRoleMenuDao.findMenuIdsByRoleIds(roleIds);
        if (menuIds==null||menuIds.size()==0)
            throw new AuthorizationException();
        //基于菜单Ids获取permission
        List<String> permissions = sysMenuDao.findPermissions(menuIds);
        //对权限信息进行封装——封装为set是因为下面的方法setStringPermissions()中的形参需要set类型的参数
        Set<String> set=new HashSet<>();
        for(String per:permissions){
            if(!StringUtils.isEmpty(per)){
                set.add(per);
            }
        }
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(set);
        return info;
    }

    @Override
    //登录验证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //1.获取用户名(用户页面输入)
        UsernamePasswordToken upToken=(UsernamePasswordToken)token;
        String username = upToken.getUsername();
        //2.基于用户名查询用户信息
        SysUser user = sysUserDao.findUserByUserName(username);
        //3.判定用户是否存在
        if(user==null)throw new UnknownAccountException();
        //4.判定用户是否已被禁用
        if(user.getValid()==0)throw new LockedAccountException();
        ///5.封装用户信息
        ByteSource credentialsSalt = ByteSource.Util.bytes(user.getSalt());//将盐值转换成credentialsSalt
        //principal凭证/身份,hashedCredentials加密的密码,credentialsSalt凭证盐,realmName
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), credentialsSalt, this.getName());
        //6.返回封装结果
        return info;//返回值会传递给认证管理器,认证管理器会通过此信息完成认证操作
    }
}

本文地址:https://blog.csdn.net/cassiel_yuan/article/details/109635372