天天看點

Springboot2(23)輕松整合shiro(帶驗證碼)Shiro配置

源碼位址

springboot2教程系列

Shiro配置

1.Spring內建Shiro一般通過xml配置,SpringBoot內建Shiro一般通過java代碼配合@Configuration和@Bean配置。

2.Shiro的核心通過過濾器Filter實作。Shiro中的Filter是通過URL規則來進行過濾和權限校驗,是以我們需要定義一系列關于URL的規則和通路權限。

3.SpringBoot內建Shiro,我們需要寫的主要是兩個類,ShiroConfiguration類,還有繼承了AuthorizingRealm的Realm類。

  • ShiroConfiguration類,用來配置Shiro,注入各種Bean。包括過濾器(shiroFilter)、安全事務管理器(SecurityManager)、密碼憑證(CredentialsMatcher)、aop注解支援(authorizationAttributeSourceAdvisor)等等
  • Realm類,包括登陸認證(doGetAuthenticationInfo)、授權認證(doGetAuthorizationInfo)

引入依賴

<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.8</java.version>
		<shiro.version>1.4.0</shiro.version>
		<commons.lang.version>2.6</commons.lang.version>
		<kaptcha.version>0.0.9</kaptcha.version>
	</properties>


	<dependencies>

		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-core</artifactId>
			<version>${shiro.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>${shiro.version}</version>
		</dependency>

		<dependency>
			<groupId>commons-lang</groupId>
			<artifactId>commons-lang</artifactId>
			<version>${commons.lang.version}</version>
		</dependency>

		<dependency>
			<groupId>com.github.axet</groupId>
			<artifactId>kaptcha</artifactId>
			<version>${kaptcha.version}</version>
		</dependency>
		
    </dependencies>
           

編寫ShiroConfiguration類

/**
 * Shiro的配置檔案
 */
@Configuration
public class ShiroConfig {

    /**
     * Session會話管理器,session交給shiro管理
     */
    @Bean
    public DefaultWebSessionManager sessionManager(@Value("${myframe.sessionTimeout:3600}") long globalSessionTimeout){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        //是否開啟會話驗證器,預設是開啟的
        sessionManager.setSessionValidationSchedulerEnabled(true);
        //禁止URL地标後面添加JSESSIONID
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        //設定全局會話逾時時間
        sessionManager.setGlobalSessionTimeout(globalSessionTimeout * 1000);
        sessionManager.setSessionValidationInterval(globalSessionTimeout * 1000);
        return sessionManager;
    }


    /**
     * 安全管理器
     * @param userRealm
     * @param sessionManager
     * @return
     */
    @Bean("securityManager")
    public SecurityManager securityManager(UserRealm userRealm, SessionManager sessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        securityManager.setRememberMeManager(null);
        securityManager.setSessionManager(sessionManager);
        return securityManager;
    }


    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        shiroFilter.setLoginUrl("/login.html");
        shiroFilter.setUnauthorizedUrl("/");

        Map<String, String> filterMap = new LinkedHashMap<>();

        filterMap.put("/public/**", "anon");
        filterMap.put("/login.html", "anon");
        filterMap.put("/sys/login", "anon");
        filterMap.put("/captcha.jpg", "anon");
        filterMap.put("/**", "authc");
        shiroFilter.setFilterChainDefinitionMap(filterMap);

        return shiroFilter;
    }

    /**
     * Shiro生命周期處理器,保證實作了Shiro内部lifecycle函數的bean執行
     * @return
     */
    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     *  啟用shrio授權注解攔截方式,AOP式方法級權限檢查
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
        proxyCreator.setProxyTargetClass(true);
        return proxyCreator;
    }

    /**
     * 加入注解的使用,不加入這個注解不生效 使用shiro架構提供的切面類,用于建立代理對象
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}

           

過濾器Filter實作

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean shiroFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new DelegatingFilterProxy("shiroFilter"));
        //該值預設為false,表示生命周期由SpringApplicationContext管理,設定為true則表示由ServletContainer管理
        registration.addInitParameter("targetFilterLifecycle", "true");
        registration.setOrder(Integer.MAX_VALUE - 1);
        registration.setEnabled(true);
        registration.addUrlPatterns("/*");
        return registration;
    }

}
           

Realm類實作

/**
 * 認證
 */
@Component
public class UserRealm extends AuthorizingRealm {
    @Resource
    private SysUserDao sysUserDao;
    
    /**
     * 授權(驗證權限時調用)
     */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		SysUserEntity user = (SysUserEntity)principals.getPrimaryPrincipal();
		Long userId = user.getUserId();
        //使用者權限清單
		Set<String> permsSet = new HashSet<>();
		String perms = sysUserDao.queryAllPerms(userId);
		permsSet.addAll(Arrays.asList(perms.trim().split(",")));
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		info.setStringPermissions(permsSet);
		return info;
	}

	/**
	 * 認證(登入時調用)
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken authcToken) throws AuthenticationException {
		UsernamePasswordToken token = (UsernamePasswordToken)authcToken;
		//查詢使用者資訊
		SysUserEntity user = sysUserDao.selectOne(token.getUsername());
		SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), getName());
		return info;
	}

}
           

登入實作類

**
 * 登入相關
 */
@Controller
public class SysLoginController {
	@Autowired
	private Producer producer;

	/**
	 * 生成驗證碼
	 * @param response
	 * @throws IOException
	 */
	@RequestMapping("captcha.jpg")
	public void captcha(HttpServletResponse response)throws IOException {
        response.setHeader("Cache-Control", "no-store, no-cache");
        response.setContentType("image/jpeg");

        //生成文字驗證碼
        String text = producer.createText();
        //生成圖檔驗證碼
        BufferedImage image = producer.createImage(text);
        //儲存到shiro session
        ShiroUtils.setSessionAttribute(Constants.KAPTCHA_SESSION_KEY, text);
        
        ServletOutputStream out = response.getOutputStream();
        ImageIO.write(image, "jpg", out);
	}
	
	/**
	 * 登入
	 */
	@ResponseBody
	@RequestMapping(value = "/sys/login", method = RequestMethod.POST)
	public R login(String username, String password, String captcha) throws IllegalAccessException {
		String kaptcha = ShiroUtils.getKaptcha(Constants.KAPTCHA_SESSION_KEY);
		if(!captcha.equalsIgnoreCase(kaptcha)){
			return R.error("驗證碼不正确");
		}
		try{
			Subject subject = ShiroUtils.getSubject();
			UsernamePasswordToken token = new UsernamePasswordToken(username, password);
			subject.login(token);
		}catch (UnknownAccountException e) {
			return R.error(e.getMessage());
		}catch (LockedAccountException e) {
			return R.error("賬号已被鎖定,請聯系管理者");
		} catch (IncorrectCredentialsException e) {
			return R.error("賬号或密碼不正确");
		}catch (AuthenticationException e) {
			return R.error("賬戶驗證失敗");
		}
		return R.ok();
	}
	
	/**
	 * 退出
	 */
	@RequestMapping(value = "logout", method = RequestMethod.GET)
	public String logout() {
		ShiroUtils.logout();
		return "redirect:login.html";
	}
	
}
           

權限測試類

@RestController
public class PermController {

    @RequestMapping("/sysRead")
    @RequiresPermissions("sys:read")
    public String sysRead(){
        return "you can sysRead";
    }

    @RequiresPermissions("sys:write")
    @RequestMapping("/sysWrite")
    public String sysWrite(){
        return "you can sysWrite";
    }

    @RequiresPermissions("sys:delete")
    @RequestMapping("/sysDelete")
    public String sysDel(){
        return "you can sysDelete";
    }
}
           

每次通路帶有@RequiresPermissions()方法時,都會進入UserRealm類的AuthorizationInfo()授權方法

另外subject.hasRole(“admin”) 或 subject.isPermitted(“admin”) 也會調用AuthorizationInfo()授權方法

登入測試(使用者密碼tom/123456)

Springboot2(23)輕松整合shiro(帶驗證碼)Shiro配置

權限測試

Springboot2(23)輕松整合shiro(帶驗證碼)Shiro配置