1. 程式人生 > >(十二)springboot中shiro的使用

(十二)springboot中shiro的使用

一、引入maven配置

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>

 

 

二、建表

使用者表、角色表、許可權表、使用者角色表、角色許可權表。

 

使用者表:

image.png

 

角色表:

image.png

 

許可權表:news:* 表示有新聞的所有許可權(包括增刪改查),而news:add,只有新聞的新增許可權。

image.png

 

使用者角色表:使用者擁有哪些角色。

image.png

 

角色許可權表:角色擁有哪些許可權。

image.png

 

三、自定義Realm

自定義realm主要用於使用者的許可權驗證以及登入驗證。

自定義realm繼承AuthorizingRealm類,並重寫doGetAuthorizationInfo和doGetAuthenticationInfo方法,doGetAuthorizationInfo方法主要校驗使用者的許可權,即該使用者擁有什麼角色以及許可權。doGetAuthenticationInfo用於校驗登入驗證,即使用者的使用者名稱或者密碼是否正確。

 

/**
 * 類名 : shiro的Realm
 * 用法 :
 * 建立人 : shyroke
 * 時間:2018/12/12 17:29
 */
public class MyRealm extends AuthorizingRealm {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PermissionMapper permissionMapper;


    /**
     * 授權,即該使用者擁有什麼角色以及許可權
     * 步驟:根據使用者名稱獲取角色以及許可權,然後設定到驗證資訊類並返回。
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        String userName = principalCollection.getPrimaryPrincipal().toString();

//        獲取該使用者的角色
        List<Role> roles = userMapper.getRolesByUserName(userName);
        Set<String> roleSets = new HashSet<>();

//        獲取該使用者的許可權集
        List<Permission> permissions = new ArrayList<>();
        Set<String> permissoinSet = new HashSet<>();

        //將List轉為Set
        if(roles !=null && roles.size()>0){
            for(Role role:roles){
                roleSets.add(role.getName());
                permissions.addAll(permissionMapper.getPermissionByRoleId(role.getId()));
            }
        }

        //將List轉為Set
        if(permissions!=null & permissoinSet.size()>0){
            for(Permission p :permissions){
                permissoinSet.add(p.getName());
            }
        }


        info.addRoles(roleSets);
        info.addStringPermissions(permissoinSet);

        return info;
    }


    /**
     * 認證,即使用者賬號密碼是否正確
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//        獲取使用者名稱和密碼
        String userName = (String)token.getPrincipal();
        String passWord = new String((char[]) token.getCredentials());

//        根據使用者名稱查詢該使用者
        User user =  userMapper.getUserByName(userName);


        if(user == null){
            throw new UnknownAccountException("使用者名稱不存在");
        }

        if(!user.getPassword().equals(passWord)){
            throw new IncorrectCredentialsException("密碼錯誤");
        }

        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getName(),user.getPassword(),getName());

        return info;
    }
}

 

 

四、shiro配置類

/**
 * 類名 :shiro的核心配置類
 * 用法 :
 * 建立人 : shyroke
 * 時間:2018/12/14 15:20
 */
@Configuration
public class ShiroConfigration {

//    設定自定義Realm
    @Bean
    public MyRealm myRealm(){
        return new MyRealm();
    }

//    許可權管理,配置主要是Realm的管理認證
    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(myRealm());
        return manager;
    }


    /**
     * 設定過濾條件和跳轉條件
     * anon 不生效的原因:1、map的型別必須是LinkedHashMap 2、anon必須定義在authc之前
     *
     * @param securityManager
     * @return
     */
    @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
            ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
            factoryBean.setSecurityManager(securityManager);

//            設定登入跳轉
            factoryBean.setLoginUrl("/admin");
            factoryBean.setSuccessUrl("/admin/index");

            //必須為LinkedHashMap 否則anon不生效
            Map<String,String> map = new LinkedHashMap<>();

            //退出
            map.put("/admin/logout","logout");

            //登入頁面和登入驗證不要攔截
            map.put("/admin/login.html","anon");
            map.put("/admin/tologin","anon");

            //設定需要過濾的連結
            map.put("/admin/**","authc");



            factoryBean.setFilterChainDefinitionMap(map);

            return factoryBean;
        }


    /**
     *  開啟Shiro的註解(如@RequiresRoles,@RequiresPermissions),需藉助SpringAOP掃描使用Shiro註解的類,並在必要時進行安全邏輯驗證
     * 配置以下兩個bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)即可實現此功能
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * 開啟aop註解支援
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

}

 

 

五、呼叫

程式碼如下,當呼叫Subject.login方法後,會呼叫自定義Realm的doGetAuthenticationInfo方法校驗使用者名稱密碼是否正確,如果不正確則丟擲對應異常,controller層捕獲並處理異常。

AuthenticationToken usernamePasswordToken = new UsernamePasswordToken(user.getName(),user.getPassword());

Subject subject = SecurityUtils.getSubject();


try {
    subject.login(usernamePasswordToken);
}catch (UnknownAccountException ex) {
    logger.info("使用者名稱錯誤!");
    return R.error("使用者名稱錯誤!");
} catch (IncorrectCredentialsException ex) {
    logger.info("密碼錯誤!");
    return R.error("密碼錯誤!");
} catch (AuthenticationException ex) {
    logger.info(ex.getMessage());
    return R.error("系統錯誤,請檢視日誌");
} catch (Exception ex) {
    logger.info(ex.getMessage());
    return R.error("系統錯誤,請檢視日誌");
}