Shiro框架使用

1,011 阅读4分钟

Shiro框架简介

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序

Apache Shiro 体系结构

image.png

1、 Authentication 认证 ---- 用户登录 2、 Authorization 授权 --- 用户具有哪些权限 3、 Cryptography 安全数据加密 4、 Session Management 会话管理 5、 Web Integration web系统集成 6、 Integrations 集成其它应用,spring、缓存框架

SpringBoot整合Shiro完成认证

分析Shiro的核心主体

Subject: 用户主体(把操作交给SecurityManager) SecurityManager:安全管理器(关联Realm) Realm:Shiro连接数据的桥梁

Maven中导入Shiro

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

自定义Realm

/**
 * 自定义Realm
 * @author dhcurry
 *
 */
public class UserRealm extends AuthorizingRealm{

	/**
	 * 执行授权逻辑
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
		System.out.println("执行授权逻辑");
		//假设数据库的用户名和密码
		String name = "eric";
		String password = "123456";
		
		//编写shiro判断逻辑,判断用户名和密码
		//1.判断用户名
		UsernamePasswordToken token = (UsernamePasswordToken)arg0;
		if(!token.getUsername().equals(name)){
			//用户名不存在
			return null;  //shiro底层会抛出UnKnowAccountException
	    }
        // 方法的前面其实就是一个Principals,存储认证成功得到的信息
        return new SimpleAuthenticationInfo(user,password,getName());

}

	/**
	 * 执行认证逻辑
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
		System.out.println("执行认证逻辑");
		return null;
	}

}

编写Shiro配置类

/**
 * Shiro的配置类
 * @author dhcurry
 *
 */
@Configuration
public class ShiroConfig {

	/**
	 * 创建ShiroFilterFactoryBean
	 */
	@Bean
	public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		
		//设置安全管理器
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		
		//添加Shiro内置过滤器
		/**
		 * Shiro内置过滤器,可以实现权限相关的拦截器
		 *    常用的过滤器:
		 *       anon: 无需认证(登录)可以访问
		 *       authc: 必须认证才可以访问
		 *       user: 如果使用rememberMe的功能可以直接访问
		 *       perms: 该资源必须得到资源权限才可以访问
		 *       role: 该资源必须得到角色权限才可以访问
		 */
		Map<String,String> filterMap = new LinkedHashMap<String,String>();
		/*filterMap.put("/add", "authc");
		filterMap.put("/update", "authc");*/
		
		filterMap.put("/testThymeleaf", "anon");
		
		filterMap.put("/*", "authc");
		
		//修改调整的登录页面
		shiroFilterFactoryBean.setLoginUrl("/toLogin");
		
		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

/**
  * 登录逻辑处理
  */
	@RequestMapping("/login")
	public String login(String name,String password,Model model){
		
		/**
		 * 使用Shiro编写认证操作
		 */
		//1.获取Subject
		Subject subject = SecurityUtils.getSubject();
		
		//2.封装用户数据
		UsernamePasswordToken token = new UsernamePasswordToken(name,password);
		
		//3.执行登录方法
		try {
			subject.login(token);
			
			//登录成功
			//跳转到test.html
			return "redirect:/testThymeleaf";
		} catch (UnknownAccountException e) {
			//e.printStackTrace();
			//登录失败:用户名不存在
			model.addAttribute("msg", "用户名不存在");
			return "login";
		}catch (IncorrectCredentialsException e) {
			//e.printStackTrace();
			//登录失败:密码错误
			model.addAttribute("msg", "密码错误");
			return "login";
		}
	}

SpringBoot整合Shiro完成授权

修改reaml的授权部分

这个部分的职责其实就是给已经认证的用户给一个授权

一般授权的方式有两种,

  1. 一种最直接,直接给用户相关操作权限——shiro中控制权限使用的是字符串,换句话说,给权限就是给用户一个字符串,比如说system:view这个字符串,而赋予用户这个字符串的任务就交给了自定义realm中的授权方法,相应的,我们在资源上设置一个需要某system:view的字符串注解——shiro中有相关注解:@RequiresPermissions(),如果用户要访问该资源,那么就必须持有相应的字符串,不然无法访问。

  2. 第二种为了管理方便,我们将一组权限赋予给某个角色,然后用户只要持有某个特定的角色就可以访问相应的资源,同样,赋予用户角色的任务就交给了realm,shiro中也有相应注解@RequiresRoles()

下面是示例代码:

public class UserRealm extends AuthorizingRealm {
    @Autowired
    private LoginService loginService;

    // 授权模块
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo info =  new SimpleAuthorizationInfo();
        info.addStringPermission("system:view");
        info.addRole("admin");
        return info;
    }

    // 认证模块
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken uptoken = (UsernamePasswordToken) authenticationToken;
        String username = uptoken.getUsername();
        String password = "";
        if(uptoken.getPassword() != null){
           password = new String(uptoken.getPassword());
        }
        int check = 0;
        try {
            check = loginService.check(username, password);
        } catch (UserNotExitingException e) {
            throw new UnknownAccountException(e.getMessage(),e);
        }catch (PasswordErrorException e){
            throw new IncorrectCredentialsException(e.getMessage(), e);
        }

        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(check,password,getName());
        return info;
    }
}

修改ShiroConfig

其他配置securityManager、ShiroFilterFactoryBean ,realm的配置这里省略,重点要加上以下代码——由于shiro使用的是AOP注解方式完成对上述两个注解的使用,这里要配置一个注解通知器才能正常使用

/**
 * Shiro的配置类
 * @author dhcurry
 *
 */
@Configuration
public class ShiroConfig {
        .......
	// 上述内容省略
        /**
     * 开启Shiro注解通知器
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            @Qualifier("securityManager") SecurityManager securityManager)
    {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

创建一个controller资源限制访问

@Controller
@RequestMapping("/back")
public class SystemController extends BaseController {

    @Autowired
    private MenuService menuService;
     
    @RequiresPermissions("system:view")
    @RequiresRoles("admin")
    @GetMapping("/index")
    public String getSystem(ModelMap modelMap){
        // 获取用户信息
        UserInfoBean user = getCurrentUser();
        // 获取菜单信息
        List<MenuVO> menus = menuService.getAllMenuForShow();

        modelMap.put("user",user);
        modelMap.put("menus", menus);
        return "back/index";
    }

}