SpringBoot整合Shiro(二)

一、shiro的工作流程

  • 項目每次啟動時,根據(jù)shiroConfig的配置,將相應(yīng)權(quán)限url加載到shiro框架中
  • 用戶執(zhí)行登錄時,會自動執(zhí)行doGetAuthenticationInfo和doGetAuthorizationInfo方法進(jìn)行認(rèn)證和鑒權(quán)
  • 用戶進(jìn)行訪問操作,若無權(quán)限或者未登錄,會根據(jù)shiroConfig的配置,自動跳轉(zhuǎn)到相應(yīng)頁面
    使用用戶賬戶名和密碼生成令牌---->執(zhí)行登錄(shiro本身并不知令牌是否合法,通過用戶自行實現(xiàn)Realm進(jìn)行比對,常用的是數(shù)據(jù)庫查詢)
Subject user = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
    user.login(token);
} catch (LockedAccountException lae) {
    token.clear();
    response.setStatus(ResponseInfo.ERROR.getStatus());
    response.setMsg("用戶已經(jīng)被鎖定不能登錄,請與管理員聯(lián)系!");
    return response;
} catch (ExcessiveAttemptsException e) {
    token.clear();
        response.setStatus(ResponseInfo.ERROR.getStatus());
    response.setMsg(" 登錄失敗次數(shù)過多,鎖定10分鐘!");
    return response;
} catch (AuthenticationException e) {
    token.clear();
    response.setStatus(ResponseInfo.ERROR.getStatus());
    response.setMsg("用戶或密碼不正確!");
    return response;
}
 // 當(dāng)驗證都通過后,把用戶信息放在session里
User loginUser = new User();
loginUser.setAccount(username);
loginUser = userMapper.selectOne(loginUser);
 if(loginUser!=null){
     Session session = SecurityUtils.getSubject().getSession();
     session.setAttribute(SessionUtil.SESSIONKEY, loginUser);
     session.setTimeout(3600000);//設(shè)置session過期時間1小時
}

注意:用戶登錄時,認(rèn)證和鑒權(quán)都已完成,之后用戶所有的操作相當(dāng)于都在shiro的監(jiān)控下

二、實現(xiàn)細(xì)節(jié)

備注:本案例所在項目是前后端分離,前端angularJs,后端springBoot,數(shù)據(jù)交互采用json,所有后臺接口返回JsonResponse型數(shù)據(jù)。
1、實現(xiàn)ShiroConfig

/**Shiro 配置*/
@Configuration
public class ShiroConfig {
     private static final Logger logger = LoggerFactory.getLogger(ShiroConfig.class);   
    @Bean
    public EhCacheManager getEhCacheManager() {  
        EhCacheManager em = new EhCacheManager();  
        em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");  
        return em;  
    }    
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName(PasswordHelper.ALGORITHMNAME);
        hashedCredentialsMatcher.setHashIterations(PasswordHelper.HASHITERATIONS);
        return hashedCredentialsMatcher;
    }
    @Bean(name = "egRealm")
    public EgRealm myShiroRealm(EhCacheManager cacheManager,HashedCredentialsMatcher hashedCredentialsMatcher) {  
        EgRealm realm = new EgRealm(); 
        realm.setCacheManager(cacheManager);
        realm.setCredentialsMatcher(hashedCredentialsMatcher);
        return realm;
    }  
    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
        daap.setProxyTargetClass(true);
        return daap;
    }
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(EgRealm myShiroRealm) {
        DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
        dwsm.setRealm(myShiroRealm);
        //<!-- 用戶授權(quán)/認(rèn)證信息Cache, 采用EhCache 緩存 --> 
        dwsm.setCacheManager(getEhCacheManager());
        return dwsm;
    }
      @Bean
    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
        aasa.setSecurityManager(securityManager);
        return aasa;
    }
       // 加載shiroFilter權(quán)限控制規(guī)則(從數(shù)據(jù)庫讀取然后配置)
    private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean,ResourceService resourceService){
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //遍歷所有需要過濾的resource_url,逐個添加到filterChainDefinitionMap中
        List<Map<String,Object>> resources = resourceService.selectAllResource();
        for(Map<String,Object> resource : resources){
            String resourceId = resource.get("resourceId").toString();
            if(StringUtils.isNotBlank(resourceId) && resource.get("resourceUrl")!=null){
                String permission = "perms["+resourceId+"]";
                filterChainDefinitionMap.put(resource.get("resourceUrl").toString(),permission);
            }
        }
        logger.info("加載resource.json文件中的資源路徑和id到shiroFilter中"); 
        //添加一些不需權(quán)限的地址
        filterChainDefinitionMap.put("/user/login", "anon");
        filterChainDefinitionMap.put("/user/toLogin", "anon");
        filterChainDefinitionMap.put("/user/getloginUserAccountName", "anon");   
        filterChainDefinitionMap.put("/metadata/**", "anon");
        filterChainDefinitionMap.put("/menu/getMenuDataByUser", "anon");
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);        
    }
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager,ResourceService resourceService) {        
        ShiroFilterFactoryBean  shiroFilterFactoryBean =  new ShiroFilterFactoryBean();
        // 必須設(shè)置 SecurityManager  
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 如果不設(shè)置默認(rèn)會自動尋找Web工程根目錄下的"/login.jsp"頁面
        shiroFilterFactoryBean.setLoginUrl("/user/toLogin");
        // 登錄成功后要跳轉(zhuǎn)的連接
        shiroFilterFactoryBean.setSuccessUrl("/user/loginSuccess");
        // 未授權(quán)界面,鑒權(quán)失敗時返回信息給前端;
        shiroFilterFactoryBean.setUnauthorizedUrl("/user/returnUnauthorizedMsg");
        loadShiroFilterChain(shiroFilterFactoryBean,resourceService);
        return shiroFilterFactoryBean;
    }

2、實現(xiàn)Realm

public class EgRealm extends AuthorizingRealm { 
    private static final Logger logger = LoggerFactory.getLogger(EgRealm.class);    
    @Autowired
    UserMapper userMapper;
    @Autowired
    UserRoleMapper userRoleMapper;
    @Autowired
    RoleResourceMapper roleResourceMapper;
    @Autowired
    ResourceMapper resourceMapper;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        logger.info("-----------------執(zhí)行Shiro權(quán)限認(rèn)證-----------------------");
        //獲取當(dāng)前登錄輸入的用戶名,等價于(String) principalCollection.fromRealm(getName()).iterator().next();
        //String loginName = (String)super.getAvailablePrincipal(principals); 
        User loginUser = (User) SecurityUtils.getSubject().getSession().getAttribute(SessionUtil.SESSIONKEY);
        if(loginUser!=null){
            // 權(quán)限信息對象info,用來存放查出的用戶的所有的角色(role)及權(quán)限(permission)
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            String userId = loginUser.getUserId();
            UserRole ur = new UserRole();
            ur.setUserId(userId);
            List<UserRole> userRoles = userRoleMapper.select(ur);
            Set<String> resourceIdSet = new HashSet<String>();
            //查詢出用戶所有具有的資源,并加入到info中
            if(userRoles!=null && userRoles.size()>0){
                for(UserRole userRole : userRoles){
                    RoleResource rr = new RoleResource();
                    rr.setRoleId(userRole.getRoleId());
                    List<RoleResource> roleResources = roleResourceMapper.select(rr);
                    if(roleResources!=null && roleResources.size()>0){
                        for(RoleResource roleResource : roleResources){
                            resourceIdSet.add(roleResource.getResourceId());
                        }
                    }
                }           
            }
            if(resourceIdSet.size()>0){
                Iterator<String> i = resourceIdSet.iterator();
                while(i.hasNext()){
                    String resourceId = i.next();
                    if(StringUtils.isNotBlank(resourceId)){
                        info.addStringPermission(resourceId);
                    }
                    
                }           
            }
            //除了添加權(quán)限,還可以添加角色,在filter中限定具有某種角色才可訪問
            //authorizationInfo.setRoles(...);
            return info;
        }
        return null;
    }
    
     /**
     * 登錄認(rèn)證
該方法主要執(zhí)行以下操作:
1、檢查提交的進(jìn)行認(rèn)證的令牌信息
2、根據(jù)令牌信息從數(shù)據(jù)源(通常為數(shù)據(jù)庫)中獲取用戶信息
3、對用戶信息進(jìn)行匹配驗證。
4、驗證通過將返回一個封裝了用戶信息的AuthenticationInfo實例。
5、驗證失敗則拋出AuthenticationException異常信息。
而在我們的應(yīng)用程序中要做的就是自定義一個Realm類,繼承AuthorizingRealm抽象類,重載doGetAuthenticationInfo
(),重寫獲取用戶信息的方法。
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        logger.info("------------執(zhí)行Shiro身份認(rèn)證--------------");
         //UsernamePasswordToken對象用來存放提交的登錄信息
        UsernamePasswordToken token=(UsernamePasswordToken) authenticationToken;
        logger.info("驗證當(dāng)前Subject時獲取到token為:" + ReflectionToStringBuilder.toString(token, ToStringStyle.MULTI_LINE_STYLE)); 
        //查出是否有此用戶
        User user = new User();
        String username = token.getUsername();
        user.setAccount(username);
        user=userMapper.selectOne(user);
        if(user!=null){
            // 若存在,將此用戶存放到登錄認(rèn)證info中,無需自己做密碼對比,Shiro會為我們進(jìn)行密碼對比校驗       // salt=username+salt
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user.getAccount(),user.getPassword(),ByteSource.Util.bytes(username+""+user.getCredentialssalt()), getName());
            return simpleAuthenticationInfo;
        }else {
            throw new UnknownAccountException();// 沒找到帳號
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,002評論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,400評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,136評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,714評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,452評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,818評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,812評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,997評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,552評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,292評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,510評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,721評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,121評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,429評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,235評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,480評論 2 379

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,825評論 18 139
  • 前言 Spring boot 是什么,網(wǎng)上的很多介紹,這里博客就不多介紹了。如果不明白Spring boot是什么...
    xuezhijian閱讀 17,937評論 13 39
  • 文章轉(zhuǎn)載自:http://blog.csdn.net/w1196726224/article/details/53...
    wangzaiplus閱讀 3,410評論 0 3
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,733評論 18 399
  • 十月的南方,我身居的小鎮(zhèn),風(fēng)中帶著沁人心脾的涼意,刺入肌骨,陰冷如蟲蟻般不動聲色的腐蝕著,天已入秋。 畢業(yè)后,我沒...
    Nancy暖昔閱讀 307評論 0 0