Springboot+shiro基於url身份認證和受權認證

shiro看了有一段時間了。可是因爲以前對這部分理解不了因此在這上面學習的進展一直很少。可是有了解權限管理在平常開發中很重要,因此硬着頭皮也要啃下來。 實現功能:html

  • 身份認證
  • 對不一樣頁面進行url受權
  • 多表登陸解決
  • 同一個頁面多role訪問

項目完整github地址 歡迎starjava

springboot一些學習整合完整地址git

shiro的四大組件:github

  • 身份認證(Authentication)-證實用戶身份,一般叫作登錄(login)。
  • 受權(Authorization)-訪問控制
  • 加密(Cryptography)-保護或隱藏數據
  • 會話管理(session management)每一個用戶時間敏感狀態

Shiro內置過濾器,能夠實現權限相關的攔截器web

  • 經常使用的過濾器:spring

  • anon: 無需認證(登陸)能夠訪問sql

  • authc: 必須認證才能夠訪問數據庫

  • user: 若是使用rememberMe的功能能夠直接訪問apache

  • perm: 該資源必須獲得資源權限才能夠訪問api

  • role: 該資源必須獲得角色權限才能夠訪問

這裏面只用到了身份認證和受權,權限認證只用到了一點點,shiro的原理是封裝的過濾器,他可以在訪問瀏覽器前能過自動完成一些內容。

shiro配置主要兩部分——shiroconfig和自定義的Realm(繼承AuthorizingRealm)。其中,shiroconfig是shiro的主要配置文件,而自定義的Realm主要是重寫AuthorizingRealm的兩個方法,分別是身份認證和受權認證調用數據庫查詢比對。而若是須要role訪問則須要重寫一個filter。

前奏

項目結構:

在這裏插入圖片描述

環境:

  • Springboot2
  • mybatis
  • shiro

新建表:

在這裏插入圖片描述

對應的bean:

package com.shiro.bean;

public class student {
    private String username;
    private String password;
    private String role;
    private String perm;
   //省略get set

複製代碼

mybatis簡單查詢:

package com.shiro.mapper;


import com.shiro.bean.student;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface studentMapper {

    @Select("select * from student where username=#{name}")
    student findByName(String name);
}

複製代碼

省略html和sql,詳細能夠到GitHub下載

頁面目錄,:

shiro核心配置文件(rolesFilter可選)。

UserRealm.java

package com.shiro.config;

import com.shiro.bean.student;
import com.shiro.mapper.studentMapper;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
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.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;


/** * 自定義Realm * @author bigsai * */
public class UserRealm extends AuthorizingRealm{

	@Autowired(required = false)
	private studentMapper studentMapper;
	private final Logger logger= LoggerFactory.getLogger(UserRealm.class);
	/** * 執行受權邏輯 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
		logger.info("執行邏輯受權");

		//給資源進行受權
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

		//添加資源的受權字符串
		//到數據庫查詢當前登陸用戶的受權字符串
		//獲取當前登陸用戶
		Subject subject = SecurityUtils.getSubject();
		student user = (student) subject.getPrincipal();
		student dbUser = studentMapper.findByName(user.getUsername());

		info.addRole(user.getRole());//添加role 和perms role表明角色 perms表明操做,或者動做等。用於顆粒化權限管理
		info.addStringPermission(dbUser.getPerm());
		System.out.println("user:"+dbUser.getPerm());
		return info;
	}
	/** * 執行認證邏輯 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
		System.out.println("執行認證邏輯");
		//編寫shiro判斷邏輯,判斷用戶名和密碼
		//1.判斷用戶名
		UsernamePasswordToken token = (UsernamePasswordToken)arg0;

		student user = studentMapper.findByName(token.getUsername());

		if(user==null){
			//用戶名不存在
			return null;//shiro底層會拋出UnKnowAccountException
		}

		//2.判斷密碼
		return new SimpleAuthenticationInfo(user,user.getPassword(),"");
	}

}

複製代碼

rolesFilter

package com.shiro.config;

import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

// AuthorizationFilter抽象類事項了javax.servlet.Filter接口,它是個過濾器。
public class rolesFilter extends AuthorizationFilter {

    @Override
    protected boolean isAccessAllowed(ServletRequest req, ServletResponse resp, Object mappedValue) throws Exception {
        Subject subject = getSubject(req, resp);
        String[] rolesArray = (String[]) mappedValue;

        if (rolesArray == null || rolesArray.length == 0) { //沒有角色限制,有權限訪問
            return true;
        }
        for (int i = 0; i < rolesArray.length; i++) {
            if (subject.hasRole(rolesArray[i])) { //若當前用戶是rolesArray中的任何一個,則有權限訪問
                return true;
            }
        }
        return false;
    }
}
複製代碼

shiroConfig:shiro的主要配置

package com.shiro.config;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;


/** * Shiro的配置類 * * @author bigsai */
@Configuration
public class ShiroConfig {

    /** * 建立ShiroFilterFactoryBean */
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        //設置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);


        Map<String, Filter> filtersMap = new LinkedHashMap<>();
        filtersMap.put("rolesFilter",new rolesFilter());
        shiroFilterFactoryBean.setFilters(filtersMap);//使用自定義fitter
        //添加Shiro內置過濾器
        /** * Shiro內置過濾器,能夠實現權限相關的攔截器 * 經常使用的過濾器: * anon: 無需認證(登陸)能夠訪問 * authc: 必須認證才能夠訪問 * user: 若是使用rememberMe的功能能夠直接訪問 * perm: 該資源必須獲得資源權限才能夠訪問 * role: 該資源必須獲得角色權限才能夠訪問 */
        Map<String, String> filterMap = new LinkedHashMap<String, String>();



        filterMap.put("/login", "anon");//要將登錄的接口放出來,否則沒權限訪問登錄的接口
        filterMap.put("/getcontroller", "anon");
//
        //受權過濾器
        //注意:當前受權攔截後,shiro會自動跳轉到未受權頁面
        filterMap.put("/add", "perms[add]");
        filterMap.put("/update", "perms[update]");

//
        filterMap.put("/test1.html","rolesFilter[admin,user]");
        filterMap.put("/*", "authc");//authc即爲認證登錄後便可訪問

        
        //修改調整的登陸頁面
        shiroFilterFactoryBean.setLoginUrl("/index");
        //設置未受權提示頁面
        shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);


        return shiroFilterFactoryBean;
    }

    /** * 建立DefaultWebSecurityManager */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //關聯realm
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    /** * 建立Realm */
    @Bean(name = "userRealm")
    public UserRealm getRealm() {
        return new UserRealm();
    }

}

複製代碼

身份認證

身份認證,就是登陸校檢。這是第一層過濾,而且當用戶沒有登陸的時候,回退到沒登錄的界面。在controller中,login的核心爲:

@RequestMapping("/login")
    public String login(String name, String password, Model model, HttpServletRequest request) {

        model.addAttribute("nama", "給個star");
        /** * 使用Shiro編寫認證操做 */
        //1.獲取Subject
        Subject subject = SecurityUtils.getSubject();

        //2.封裝用戶數據
        UsernamePasswordToken token = new UsernamePasswordToken(name, password);

        //3.執行登陸方法
        try {
            subject.login(token);

            //登陸成功
            //跳轉
            return "redirect:/index2";
        } catch (UnknownAccountException e) {
            //e.printStackTrace();
            //登陸失敗:用戶名不存在
            model.addAttribute("msg", "用戶名不存在");
            return "login";
        } catch (IncorrectCredentialsException e) {
            //e.printStackTrace();
            //登陸失敗:密碼錯誤
            model.addAttribute("msg", "密碼錯誤");
            return "login";
        }
    }
複製代碼

releam中

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
		System.out.println("執行認證邏輯");
		//編寫shiro判斷邏輯,判斷用戶名和密碼
		//1.判斷用戶名
		UsernamePasswordToken token = (UsernamePasswordToken)arg0;

		student user = studentMapper.findByName(token.getUsername());
		if(user==null){
			//用戶名不存在
			return null;//shiro底層會拋出UnKnowAccountException
		}
		//2.判斷密碼
		return new SimpleAuthenticationInfo(user,user.getPassword(),"");
	}
複製代碼

而這只是表象的處理過程,而在releam(繼承AuthorizingRealm)中須要充血doGetAuthenticationInfo()方法.

大體流程爲:登陸——>拿帳號密碼檢驗———>用着token的帳號經過你的sql查詢對象——>比對數據是否一致——>經過仍是拋各類異常

而在shiroConfig中,基於url過濾時authc便可訪問

多表登陸源如何操做?

可能會遇到以下狀況:教師端,學生端來自兩張表,兩個登陸接口,我該如何使用shiro身份認證。對於這種問題,你能夠配置多個releam,可是我以爲若是簡單你能夠在不一樣的登陸接口下傳遞一個參數過來,這個參數就用session傳遞。由於,shiro的session和網頁httprequest得到的session是同一個session

因此當你在login傳遞一個屬性到releam中,可用 if else判斷而後不一樣登陸接口執行不一樣的查詢方法便可。

受權管理

接上流程 是否登陸——>是/否——(是)—>查詢role/perm添加到subject——>過濾器校驗該url須要權限——>能夠訪問/權限不足

shiro主要url能夠根據角色(role)和資源(perm)的管理。對於role,能夠是管理員,教師等,而perm,多是一個動做,一個操做,等等。==而且可能一個角色擁有多個role和perm==。 同理,受權就是查詢數據庫的role或者perm字段添加到角色中。固然具體api不作介紹。 主要方法爲上述:

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
		logger.info("執行邏輯受權");

		//給資源進行受權
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

		//添加資源的受權字符串
		//到數據庫查詢當前登陸用戶的受權字符串
		//獲取當前登陸用戶
		Subject subject = SecurityUtils.getSubject();
		student user = (student) subject.getPrincipal();
		student dbUser = studentMapper.findByName(user.getUsername());

		info.addRole(user.getRole());//添加role 和perms role表明角色 perms表明操做,或者動做等。用於顆粒化權限管理
		info.addStringPermission(dbUser.getPerm());
		System.out.println("user:"+dbUser.getPerm());
		return info;
	}
複製代碼

而url中也是

filterMap.put("/add", "perms[add]");
 filterMap.put("/update", "roles[admin]");
複製代碼

如何解決界面多角色/資源問題

經常遇到這種狀況:一個接口/頁面,有兩個或者以上角色能夠訪問。而後再後臺的過濾器配置總。shiro默認的配置是and而不是or。這就須要咱們本身定義filter繼承AuthorizationFilter從寫對應方法。

以多角色訪問爲例子。從寫上述就是文件rolesFilter。在使用的時候也要首先聲明filter才能使用。

在這裏插入圖片描述

訪問效果

在頁面受權的

運行測試:訪問其餘接口都被返回到這個界面

在這裏插入圖片描述
在這裏插入圖片描述

登錄成功後,界面能夠訪問

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
有個小注意點:若是mybatis2.0版本回和spring-start-web有衝突。我用1.3.2版本沒問題。

項目github地址

springboot一些學習整合完整地址

相關文章
相關標籤/搜索