SpringBoot (七) :Springboot與權(quán)限認(rèn)證

上一篇寫了springboot與redis集群的整合,接下來(lái)我們寫一個(gè)通用業(yè)務(wù)相關(guān)的功能,本來(lái)打算寫elasticsearch相關(guān)的整合的,但是這塊環(huán)境我還不太熟悉,需要了解一下再繼續(xù),以后肯定會(huì)寫的。
我這里打算做成一個(gè)開箱即用的開源系統(tǒng),所以也會(huì)從基礎(chǔ)業(yè)務(wù)開始寫起。

1、權(quán)限認(rèn)證

開始還是先寫一些基礎(chǔ)知識(shí),權(quán)限主要用于資源和用戶的一些校驗(yàn)。
對(duì)于java項(xiàng)目,常用的權(quán)限校驗(yàn)的組件主要有shiro和security,而shiro由于功能簡(jiǎn)單,上手快而很快的被程序員喜愛。這里我們也以shiro為主,參考 shiro官網(wǎng),
我覺得學(xué)習(xí)一門新的東西,最快的就是參考官方文檔。
開濤老師的shiro系列也是非常的贊,跟我學(xué)shiro系列

2、框架的搭建

首先還是在Spring官網(wǎng) 搭建基礎(chǔ)配置
項(xiàng)目中集成了jwt,但是這里還沒有使用,這次只是簡(jiǎn)單的把項(xiàng)目搭建起來(lái),做簡(jiǎn)單的測(cè)試。


pom.xml

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- mysql相關(guān)-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- mybatis相關(guān)-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <!-- Spring 整合Shiro需要的依賴 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>
        <!-- 日志依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>

3、shiroConfig配置

@Configuration
public class ShiroConfig {
    /**
     * ShiroFilterFactoryBean 處理攔截資源文件問題。
     * 注意:?jiǎn)为?dú)一個(gè)ShiroFilterFactoryBean配置是或報(bào)錯(cuò)的,以為在
     * 初始化ShiroFilterFactoryBean的時(shí)候需要注入:SecurityManager
     * Filter Chain定義說明 1、一個(gè)URL可以配置多個(gè)Filter,使用逗號(hào)分隔 2、當(dāng)設(shè)置多個(gè)過濾器時(shí),全部驗(yàn)證通過,才視為通過
     * 3、部分過濾器可指定參數(shù),如perms,roles
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        factoryBean.setSecurityManager(securityManager);
        factoryBean.getFilters().put("authControlFilter", authControlFilter());
        //攔截器.
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
        filterChainDefinitionMap.put("/*", "authControlFilter");
        filterChainDefinitionMap.put("/favicon.ico", "anon");
        factoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return factoryBean;
    }

    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setSubjectFactory(subjectFactory());
        securityManager.setSessionManager(sessionManager());
        securityManager.setRealm(authRealm());
        /*
         * 禁用使用Sessions 作為存儲(chǔ)策略的實(shí)現(xiàn),但它沒有完全地禁用Sessions
         * 所以需要配合context.setSessionCreationEnabled(false);
         */
        ((DefaultSessionStorageEvaluator)((DefaultSubjectDAO)securityManager.getSubjectDAO()).getSessionStorageEvaluator()).setSessionStorageEnabled(false);
        return securityManager;
    }


    /**
     * Add.2.1
     * subject工廠管理器.
     * @return
     */
    @Bean
    public DefaultWebSubjectFactory subjectFactory(){
        StatelessDefaultSubjectFactory subjectFactory = new StatelessDefaultSubjectFactory();
        return subjectFactory;
    }

    /**
     * Add.2.4
     * session管理器:
     * sessionManager通過sessionValidationSchedulerEnabled禁用掉會(huì)話調(diào)度器,
     * 因?yàn)槲覀兘玫袅藭?huì)話,所以沒必要再定期過期會(huì)話了。
     * @return
     */
    @Bean
    public DefaultSessionManager sessionManager(){
        DefaultSessionManager sessionManager = new DefaultSessionManager();
        sessionManager.setSessionValidationSchedulerEnabled(false);
        return sessionManager;
    }
    /**
     * 身份認(rèn)證realm; (這個(gè)需要自己寫,賬號(hào)密碼校驗(yàn);權(quán)限等)
     *
     * @return
     */
    @Bean
    public AuthRealm authRealm() {
        AuthRealm authRealm = new AuthRealm();
        return authRealm;
    }

    /**
     * Add.4.1
     * 訪問控制器.
     * @return
     */
    @Bean
    public AuthControlFilter authControlFilter(){
        AuthControlFilter authControlFilter = new AuthControlFilter();
        return authControlFilter;
    }
}

備注:這里有很多的坑,網(wǎng)上很多的代碼根本跑步起來(lái)啊,希望小伙伴們寫博客的時(shí)候,把代碼至少自己跑一遍,首先我這里使用的是idea編輯器,eclipse沒有試。

  • 1、filterChainDefinitionMap.put("/favicon.ico", "anon"); 這行代碼一定要配置,切記。不然會(huì)執(zhí)行多次
  • shiroFilter:主要做資源的攔截,注意所有的請(qǐng)求都會(huì)被 ShiroFilter 攔截并進(jìn)行相應(yīng)的鏈?zhǔn)教幚?,可以說是所有配置的入口,簡(jiǎn)化配置,方便使用。

5、Realm的配置

shiro的認(rèn)證過程最終會(huì)交由Realm執(zhí)行,這時(shí)會(huì)調(diào)用Realm的getAuthenticationInfo(token)方法。

public class AuthRealm extends AuthorizingRealm {
    private static final Logger logger = LoggerFactory.getLogger(AuthorizingRealm.class);
    private static final String USER_NAME = "admin";
    private static final String PASSWORD = "123456";


    /**
     * 僅支持StatelessToken 類型的Token,
     * 那么如果在StatelessAuthcFilter類中返回的是UsernamePasswordToken,那么將會(huì)報(bào)如下錯(cuò)誤信息:
     * Please ensure that the appropriate Realm implementation is configured correctly or
     * that the realm accepts AuthenticationTokens of this type.StatelessAuthcFilter.isAccessAllowed()
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof StatelessAuthenticationToken;
    }
    /**
     * 認(rèn)證信息.(身份驗(yàn)證) : Authentication 是用來(lái)驗(yàn)證用戶身份
     * 授權(quán)
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        logger.info("StatelessRealm.doGetAuthorizationInfo()");
        //根據(jù)用戶名查找角色,請(qǐng)根據(jù)需求實(shí)現(xiàn)
        String username = (String) principalCollection.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

        //這里模擬admin賬號(hào)才有role的權(quán)限.
        if("admin".equals(username)){
            authorizationInfo.addRole("0");
        }
        return authorizationInfo;
    }

    /**
     * 登錄驗(yàn)證
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        logger.info("authRealm.doGetAuthenticationInfo()");

        StatelessAuthenticationToken statelessToken = (StatelessAuthenticationToken)authenticationToken;
        String username = (String)statelessToken.getPrincipal();//不能為null,否則會(huì)報(bào)錯(cuò)的.

        //根據(jù)用戶名獲取密鑰(和客戶端的一樣)
        //在服務(wù)器端生成客戶端參數(shù)消息摘要
        String serverDigest = DecriptUtil.MD5(USER_NAME+PASSWORD);
        logger.info("{$serverDigest}:{}",serverDigest);
        logger.info("---------------->"+serverDigest+","+statelessToken.getCredentials());
        //然后進(jìn)行客戶端消息摘要和服務(wù)器端消息摘要的匹配
        SimpleAuthenticationInfo  authenticationInfo = new SimpleAuthenticationInfo(
                statelessToken.getCredentials(),
                serverDigest,
                getName());
        return authenticationInfo;
    }

    //得到密鑰,此處硬編碼一個(gè).
    private String getKey(String username) {
        return username;
    }
}

接下來(lái)還有DefaultWebSubjectFactory和AuthenticationToken的配置,這里就不多做說明了,具體可以運(yùn)行代碼看看lessons-7
github

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,267評(píng)論 25 708
  • 學(xué)習(xí)任務(wù)目標(biāo) 用戶必須要登陸之后才能訪問定義鏈接,否則跳轉(zhuǎn)到登錄頁(yè)面。 對(duì)鏈接進(jìn)行權(quán)限控制,只有當(dāng)當(dāng)前登錄用戶有這...
    z77z閱讀 71,432評(píng)論 39 274
  • 好友向我闡述了一個(gè)夢(mèng):夢(mèng)中見到了她高中時(shí)期就已經(jīng)過身的長(zhǎng)輩,當(dāng)時(shí)長(zhǎng)輩有70多高齡了,行動(dòng)不便,只見長(zhǎng)輩躺在...
    我是任意東西閱讀 309評(píng)論 0 0
  • 夏天出生的時(shí)候是夏天,農(nóng)歷六月,正是盛夏。因?yàn)槭窍奶斐錾?,所以我給他取名夏天,希望他能光明璀璨,所以單名熠...
    慕魘閱讀 936評(píng)論 0 5
  • 我比較內(nèi)向,每天面對(duì)著孤獨(dú)只有自己給自己傾訴,在操場(chǎng)上走一圈,我可以想很多的東西出來(lái),我喜歡自己一個(gè)人靜靜去思考著...
    小肥楊閱讀 257評(píng)論 0 0

友情鏈接更多精彩內(nèi)容