Spring Boot整合shiro

1、shiro是什么

用官方的話說Apache Shiro是一個(gè)功能強(qiáng)大且易于使用的Java安全框架,它為開發(fā)人員提供了一種直觀而全面的解決方案,用于身份驗(yàn)證,授權(quán),加密和會(huì)話管理。
首先我們先看官方給出的Shiro外部結(jié)構(gòu)圖,看shiro是如何完成工作的:


官方結(jié)構(gòu)圖.png

shiro主要有三大功能模塊:

Subject:

應(yīng)用代碼直接交互的對(duì)象Subject,也就是說Shiro的對(duì)外API核心就是Subject,代表了當(dāng)前的用戶,也不一定是一個(gè)具體的人,也當(dāng)前應(yīng)用交互的任何東西都是Subject,與Subject的所有交互都會(huì)委托給SecurityManager,Subject其實(shí)就是一個(gè)門面,SecurityManager才是實(shí)際的執(zhí)行者

SecurityManager:

安全管理器,即所有與安全有關(guān)的操作都會(huì)與SecurityManager交互,并且它管理著所有的Sbject,它才是Shiro的核心,負(fù)責(zé)與Shiro的其他組件進(jìn)行交互

Realms:

Shiro從Realm獲取安全數(shù)據(jù)(用戶,角色,權(quán)限),就是說SecurityManager要驗(yàn)證用戶身份,它就需要從Realm獲取相應(yīng)的用戶進(jìn)行比較,來確定用戶的身份是否合法,也需要用Realm得到用戶相應(yīng)的角色、權(quán)限,進(jìn)行驗(yàn)證用戶的操作是否能夠進(jìn)行

2、集成Shiro

1、導(dǎo)入jar包

在pom文件中添加shiro依賴

 <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.6.0</version>
        </dependency>
2、編寫配置類

創(chuàng)建ShiroConfig類

@Configuration
public class ShiroConfig {
    //ShiroFilterFactoryBean
    //DefaultWebSecurityManager
    //創(chuàng)建realm對(duì)象
}

這三個(gè)就對(duì)應(yīng)我們剛剛說的三個(gè)模塊,然后就開始配置這三個(gè)對(duì)象,我們先從realm開始配置,realm對(duì)象需要我們自己先定義,我們先新建一個(gè)ShiroRealm類然后繼承 AuthorizingRealm,然后重寫他的方法,一個(gè)授權(quán),一個(gè)認(rèn)證

//自定義realm 需要繼承 AuthorizingRealm
public class ShiroRealm extends AuthorizingRealm {
    //授權(quán)
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }
    //認(rèn)證
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        return null;
    }
}

然后返回ShiroConfig去創(chuàng)建realm對(duì)象

@Configuration
public class ShiroConfig {
    //ShiroFilterFactoryBean
    //DefaultWebSecurityManager

    //創(chuàng)建realm對(duì)象
    @Bean
    public ShiroRealm shiroRealm(){
        return new ShiroRealm();
    }
}

這樣我們寫的對(duì)象就被spring托管了,接下來我們寫DefaultWebSecurityManager,因?yàn)镈efaultWebSecurityManager需要Realm

@Configuration
public class ShiroConfig {
    //ShiroFilterFactoryBean
    
    //DefaultWebSecurityManager
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //關(guān)聯(lián)realm
        securityManager.setRealm(shiroRealm);
        return securityManager;
    }

    //創(chuàng)建realm對(duì)象
    @Bean
    public ShiroRealm shiroRealm(){
        return new ShiroRealm();
    }
}

接下來寫ShiroFilterFactoryBean

@Configuration
public class ShiroConfig {
    //ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager manager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //設(shè)置安全管理器
        bean.setSecurityManager(manager);
        return bean;
    }
    //DefaultWebSecurityManager
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //關(guān)聯(lián)realm
        securityManager.setRealm(shiroRealm);
        return securityManager;
    }

    //創(chuàng)建realm對(duì)象
    @Bean
    public ShiroRealm shiroRealm(){
        return new ShiroRealm();
    }
}

3、設(shè)置過濾器

前面我們已經(jīng)把shiro初步集成了,接下來就需要配置過濾器

//ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager manager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //設(shè)置安全管理器
        bean.setSecurityManager(manager);
        /*
            添加shiro的內(nèi)置過濾器
            anon:  無(wú)需認(rèn)證就可訪問
            authc: 必須認(rèn)證才能訪問
            user:  必須擁有 記住我功能才可訪問
            perms: 擁有對(duì)某個(gè)資源的權(quán)限才能訪問
            role:  擁有某個(gè)角色權(quán)限才能訪問
         */
        Map<String,String>filterMap = new HashMap<>();
        filterMap.put("/version/*","authc");
        filterMap.put("/api/*","anon");
        bean.setFilterChainDefinitionMap(filterMap);
        //設(shè)置登錄請(qǐng)求
        bean.setLoginUrl("/home/toLogin");
        return bean;
    }

這樣配置的意思是路徑/version/是需要認(rèn)證才可訪問,/api/無(wú)需認(rèn)證就可訪問,然后沒認(rèn)證的返回登錄頁(yè)面去認(rèn)證,接下來試試,在網(wǎng)頁(yè)中輸入網(wǎng)址


image.png

因?yàn)闆]認(rèn)證過直接跳轉(zhuǎn)到了登錄頁(yè)面,這樣就算成功了


image.png

4、實(shí)現(xiàn)登錄功能

現(xiàn)在我們已經(jīng)成功攔截了我們?cè)O(shè)置的路徑,然后就要實(shí)現(xiàn)登錄通過認(rèn)證,首先寫一個(gè)用戶實(shí)體類

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Admin {
    private Integer id;
    //用戶名稱
    private String name;
    //登錄帳號(hào)
    private String username;
    //登錄密碼
    private String password;
    //創(chuàng)建時(shí)間
    private Date createTime;
}

然后寫登錄方法

    @PostMapping("login")
    public String login(String adminName, String password, Model model) {
        Admin admin = adminMapper.searchByAccount(adminName);
        if (admin == null){
            model.addAttribute("error","帳號(hào)密碼錯(cuò)誤");
            return "home/login";
        }
        if (!admin.getPassword().equals(password)){
            model.addAttribute("error","帳號(hào)密碼錯(cuò)誤");
            return "home/login";
        }
        //獲取當(dāng)前用戶
        Subject subject = SecurityUtils.getSubject();
        //封裝用戶的登錄數(shù)據(jù)
        UsernamePasswordToken token = new UsernamePasswordToken(admin.getUsername(),admin.getPassword());
        try {
            subject.login(token);  //執(zhí)行登錄方法
            return "redirect:/version/list";
        } catch (Exception e){ 
            e.printStackTrace();
        }
        return "home/login";
    }

這樣我們?cè)谡?qǐng)求登錄請(qǐng)求后他會(huì)去執(zhí)行doGetAuthenticationInfo方法做認(rèn)證操作,那我們就去里面寫認(rèn)證操作

 //認(rèn)證
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String adminName = token.getUsername();
        if (adminName != null && !adminName.equals("")){
            Admin admin = adminMapper.searchByAccount(adminName);
            if (admin != null){
                return new SimpleAuthenticationInfo(admin.getUsername(),admin.getPassword(),getName());
            }
        }
        return null;
    }

如果查詢數(shù)據(jù)庫(kù)沒有該用戶return null后會(huì)拋出一個(gè)UnknownAccountException異常,成功返回AuthenticationInfo的實(shí)現(xiàn)類
這樣我們就實(shí)現(xiàn)了登錄認(rèn)證功能

?著作權(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ù)。

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