搭建springboot-shiro

  • 開發環境配置
  1. eclipse Mars.2 Release (4.5.2)
  2. maven 3.3.9
  3. jdk 1.8.0_171
  4. os win10
  • 環境搭建
    1. 創建maven工程
    2. 編輯pom.xml
    <!-- 繼承說明:這裏繼承SpringBoot提供的父工程 -->
    <parent>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-parent</artifactId>
    	<version>2.0.0.RELEASE</version>
    </parent>
    
    <dependencies>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-web</artifactId>
    	</dependency>
    
    	<dependency>
    		<groupId>org.apache.shiro</groupId>
    		<artifactId>shiro-spring-boot-starter</artifactId>
    		<version>1.4.0</version>
    	</dependency>
    </dependencies>
    
    1. 創建包結構
      springboot-shiro包結構
    2. ShiroConfig配置類
    @Configuration
    public class ShiroConfig {
    	/**
    	 * ShiroFilterFactoryBean 處理攔截資源文件問題。設置對應的過濾條件和跳轉條件
    	 */
    	@Bean
    	public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
    		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    
    		// 必須設置 SecurityManager
    		shiroFilterFactoryBean.setSecurityManager(securityManager);
    
    		// 如果不設置默認會自動尋找Web工程根目錄下的"/login.jsp"頁面
    		shiroFilterFactoryBean.setLoginUrl("/login");
    		// 登錄成功後要跳轉的鏈接
    		shiroFilterFactoryBean.setSuccessUrl("/index");
    		// 未授權界面;
    		shiroFilterFactoryBean.setUnauthorizedUrl("/403");
    
    		// 自定義攔截器
    		Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
    		// 限制同一帳號同時在線的個數。
    		// filtersMap.put("kickout", kickoutSessionControlFilter());
    		shiroFilterFactoryBean.setFilters(filtersMap);
    
    		// 權限控制map.
    		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
    		// 授權資源,只有登錄了才能訪問,並且有該對應權限的用戶纔可以訪問
    		// 例如:從數據庫獲取動態的權限
    		// filterChainDefinitionMap.put("/add", "perms[權限添加]");
    		// 從數據庫獲取
    		/*
    		 * List<SysPermissionInit> list = sysPermissionInitService.selectAll();
    		 * 
    		 * for (SysPermissionInit sysPermissionInit : list) {
    		 * filterChainDefinitionMap.put(sysPermissionInit.getUrl(),
    		 * sysPermissionInit.getPermissionInit()); }
    		 */
    
    		// <!-- 過濾鏈定義,從上向下順序執行,一般將 /**放在最爲下邊 -->:這是一個坑呢,一不小心代碼就不好使了;
    		// <!-- authc:所有url都必須認證通過纔可以訪問; anon:所有url都都可以匿名訪問-->
    		// logout這個攔截器是shiro已經實現好了的。
    		// filterChainDefinitionMap.put("/js/**","anon");
    		// filterChainDefinitionMap.put("/login/**","anon");
    		// filterChainDefinitionMap.put("/logout","logout");
    		// filterChainDefinitionMap.put("/**","authc");
    
    		// 遊客,開發權限
    		filterChainDefinitionMap.put("/guest/**", "anon");
    		// 用戶,需要角色權限 "user"
    		filterChainDefinitionMap.put("/user/**", "roles[user],perms[retrieve]");
    		// 管理員,需要角色權限 "admin"
    		filterChainDefinitionMap.put("/admin/**", "roles[admin],perms[create]");
    		// 開放登陸接口
    		filterChainDefinitionMap.put("/login", "anon");
    		// 其餘接口一律攔截
    		// 主要這行代碼必須放在所有權限設置的最後,不然會導致所有 url 都被攔截
    		filterChainDefinitionMap.put("/**", "authc");
    
    		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    		return shiroFilterFactoryBean;
    	}
    
    	/**
    	 * 權限管理,配置主要是Realm的管理認證
    	 * 
    	 * @return
    	 */
    	@Bean
    	public DefaultWebSecurityManager securityManager() {
    		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    		// 設置realm.
    		securityManager.setRealm(myShiroRealm());
    		// 自定義緩存實現
    		// securityManager.setCacheManager(cacheManager());
    		// 自定義session管理
    		// securityManager.setSessionManager(sessionManager());
    		// 注入記住我管理器;
    		securityManager.setRememberMeManager(rememberMeManager());
    		return securityManager;
    	}
    
    	/**
    	 * 密碼匹配憑證管理器
    	 */
    	@Bean(name = "hashedCredentialsMatcher")
    	public HashedCredentialsMatcher hashedCredentialsMatcher() {
    		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
    		hashedCredentialsMatcher.setHashAlgorithmName(PasswordUtils.ALGORITHM_NAME);
    		hashedCredentialsMatcher.setHashIterations(PasswordUtils.HASH_ITERATIONS);// 散列的次數,比如散列兩次,相當於
    																					// md5(md5(""));
    		return hashedCredentialsMatcher;
    	}
    
    	/**
    	 * 自定義的Realm,將自己的驗證方式加入容器
    	 */
    	@Bean
    	public MyShiroRealm myShiroRealm() {
    		MyShiroRealm myShiroRealm = new MyShiroRealm();
    		// 設置密碼憑證匹配器
    //		myShiroRealm.setCredentialsMatcher(matcher);
    		return myShiroRealm;
    	}
    
    	/**
    	 * cookie對象;
    	 * 
    	 * @return
    	 */
    	public SimpleCookie rememberMeCookie() {
    		// 這個參數是cookie的名稱,對應前端的checkbox的name = rememberMe
    		SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
    		// <!-- 記住我cookie生效時間30天 ,單位秒;-->
    		simpleCookie.setMaxAge(2592000);
    		return simpleCookie;
    	}
    
    	/**
    	 * cookie管理對象;記住我功能
    	 * 
    	 * @return
    	 */
    	public CookieRememberMeManager rememberMeManager() {
    		CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
    		cookieRememberMeManager.setCookie(rememberMeCookie());
    		// rememberMe cookie加密的密鑰 建議每個項目都不一樣 默認AES算法 密鑰長度(128 256 512 位)
    		cookieRememberMeManager.setCipherKey(Base64.decode("3AvVhmFLUs0KTA3Kprsdag=="));
    		return cookieRememberMeManager;
    	}
    
    	/**
    	 * 開啓shiro aop註解支持. 使用代理方式;所以需要開啓代碼支持;否則@RequiresRoles等註解無法生效
    	 * 
    	 * @param securityManager
    	 * @return
    	 */
    	@Bean
    	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
    			DefaultSecurityManager securityManager) {
    		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
    		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
    		return authorizationAttributeSourceAdvisor;
    	}
    
    	/**
    	 * Shiro生命週期處理器
    	 * 
    	 * @return
    	 */
    	@Bean
    	public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
    		return new LifecycleBeanPostProcessor();
    	}
    
    	/**
    	 * 自動創建代理(切面攔截)
    	 * 
    	 * @return
    	 */
    	@Bean
    	@DependsOn({ "lifecycleBeanPostProcessor" })
    	public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
    		DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
    		advisorAutoProxyCreator.setProxyTargetClass(true);
    		return advisorAutoProxyCreator;
    	}
    }
    
    1. 自定義驗證方式類MyShiroRealm
public class MyShiroRealm extends AuthorizingRealm {

	@Autowired
	private IUserService iUserService;

	/**
	 * 授權用戶權限
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
		// 獲取用戶
		// User user = (User)SecurityUtils.getSubject().getPrincipal();

		// 獲取登錄用戶名
		String name = (String) principalCollection.getPrimaryPrincipal();

		// 查詢用戶名稱
		User user = iUserService.getByUsername(name);

		// 添加角色和權限
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		
		for (Role role : iUserService.getRoles(user.getId())) {
			// 添加角色
			info.addRole(role.getMark());
			for (Permission permission : iUserService.getRolePermissions(role.getId())) {
				// 添加權限
				info.addStringPermission(permission.getMark());
			}
		}

		return info;
	}

	/**
	 * 驗證用戶身份
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		// 獲取用戶賬號
		String username = token.getPrincipal().toString();

		User user = iUserService.getByUsername(username);

		//密碼進行加密處理
		String password = user.getPassword();
		if (password != null) {
			AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, // 認證通過後,存放在session,一般存放user對象
					password, // 用戶數據庫中的密碼
					getName()); // 返回Realm名
			return authenticationInfo;
		} else {
			return null;
		}

	}

}
  1. 配置LoginController
@RestController
public class LoginController {
	@Autowired
	private IUserService userService;
	
	@RequestMapping(value = "/login", method = RequestMethod.GET)
    public String login(String username, String password) {
		// 從SecurityUtils裏邊創建一個 subject
        Subject subject = SecurityUtils.getSubject();
		
		// 在認證提交前準備 token(令牌)
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
	
        // 執行認證登陸
        subject.login(token);
        
        //根據權限,指定返回數據
        String role = userService.getRoles("110").get(0).getMark();
        
        if ("user".equals(role)) {
            return "歡迎登陸";
        }
        
        if ("admin".equals(role)) {
            return "歡迎來到管理員頁面";
        } 
        
        return "權限錯誤!";
	}
}

7.配置UserController類

@RestController
@RequestMapping("/user")
public class UserController {

	//註解的使用
    @RequiresRoles("user")
    @RequiresPermissions("retrieve")
    @RequestMapping(value = "/retrieve")
    public String retrieve(){
        return "Retrieve success!";
    }

}

8.配置AdminController類

@RestController
@RequestMapping("/admin")
public class AdminController {

	//註解的使用
    @RequiresRoles("admin")
    @RequiresPermissions("create")
    @RequestMapping(value = "/create")
    public String create(){
        return "Create success!";
    }

}

9.編寫啓動代碼

@SpringBootApplication
public class Application {
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

10.瀏覽器運行 http://localhost:8080/user/retrieve 會跳轉到 http://localhost:8080/login 因爲訪問時會先跳轉到/login進行校驗,但是沒有帶用戶名和密碼所以校驗失敗
shiro驗證失敗
11.瀏覽器運行http://localhost:8080/login?username=lc&password=1314 驗證成功
shiro驗證成功

12.瀏覽器運行http://localhost:8080/admin/create 訪問權限驗證成功
shiro訪問權限驗證成功
13.瀏覽器運行http://localhost:8080/user/retrieve 因爲訪問權限驗證不通過所以會跳轉到配置的403 http://localhost:8080/403 但是沒有配置403請求處理所以404
shiro訪問權限驗證不通過
ps:沒有配置數據庫請求服務層裏的數據都是造的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章