SpringBoot整合Shiro

SpringBoot整合Shiro使用

哪里不懂点哪里官网:http://shiro.apache.org/

一、简介

 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。

 Java领域中spring security(原名Acegi)也是一个开源的权限管理框架,但是spring security依赖spring运行,而shiro就相对独立,最主要是因为shiro使用简单、灵活,所以现在越来越多的用户选择shiro

tips:1.4版本前后有区别的,需注意一下

关键词介绍

Subject

Subject即主体,外部应用与subject进行交互,subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。Subject在shiro中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权

SecurityManager

SecurityManager即安全管理器,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。

SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。

Authenticator

Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。

Authorizer

Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。

realm

Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。

注意:不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。

sessionManager

sessionManager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。

SessionDAO

SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。

CacheManager

CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。

Cryptography

Cryptography即密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。

二、使用(基于springboot)

 1、pom依赖

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

 2、简约yml配置,按需配置即可

shiro:
  web:
    enabled: true #表示开启 shiro
  unauthorizedUrl: /doLogin #访问未获授权的页面时默认的跳转路径如404,401
  successUrl: /hello #登录成功的跳转页面
  loginUrl: /login #登录页面

 3、ShiroConfig注入

@Configuration
public class ShiroConfig {
    //创建realm对象,需要自定义类:1
    @Bean
    public MyRealm myRealm() {
        return new MyRealm();
    }

    //DefaultWebSecurityManager:2
    @Bean
    public DefaultWebSecurityManager securityManager(@Qualifier("myRealm") MyRealm myRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myRealm);
        return securityManager;
    }

    //ShiroFilterFactoryBean:3
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(securityManager);
        //添加shiro内置过滤器
        Map <String, String> filterMap = new LinkedHashMap <>();
        //anon: 无需认证即可访问
        //authc: 需要认证才可访问
        //user: 点击“记住我”功能可访问
        //perms: 拥有权限才可以访问
        //role: 拥有某个角色权限才能访问
        filterMap.put("/api/*", "authc");
        bean.setFilterChainDefinitionMap(filterMap);
        bean.setLoginUrl("/doLogin");
        return bean;
    }
}

 4、自定义Realm类

@Slf4j
public class MyRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        log.info("鉴权");//用户--(关系)--角色--(关系)--权限(数据库或缓存获取匹配)
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //Admin admin = (Admin) principalCollection.getPrimaryPrincipal();
        //Role role = adminService.getRoleName(admin.getId());
        Set <String> permission = new HashSet <>();//比数组快,不用一个个遍历索引去访问
        if(true) {
            info.addRole("admin");
            permission.add("111");
            permission.add("333");
        } else {
            info.addRole("user");
            permission.add("222");
        }
        info.addStringPermissions(permission);
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        log.info("认证");
        String username = (String) token.getPrincipal();
        Admin userLogin = new Admin();//数据库或缓存取用户信息匹配
        userLogin.setUserName("admin");
        userLogin.setPassword("111111");
        userLogin.setIsLock(0);
        if(userLogin == null) {
            throw new UnknownAccountException("账户不存在!");
        }
        if(userLogin.getIsLock() == 1) {
            log.info("当前登录的用户被锁定");
            throw new LockedAccountException();//抛出异常
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userLogin, DigestUtils.md5DigestAsHex(userLogin.getPassword().getBytes()), ByteSource.Util.bytes(userLogin.getUserName()), getName());
        userLogin.setPassword("");
        //将用户信息放入session中
        SecurityUtils.getSubject().getSession().setAttribute(GlobalConstant.SESSION_USER_INFO, userLogin);
        return authenticationInfo;
    }
}

 5、实际操作

  a、接口添加注解,可选(问题是需要控制器统一捕捉异常,看实际需要)  

    @RequiresRoles(value = "admin", logical = Logical.OR)
    @RequiresPermissions(value ="222",logical=Logical.OR)
    @RequiresAuthentication
    @RequiresUser

  b、使用拦截器,实现 HandlerInterceptor 接口重写 preHandle

@Slf4j
public class IsLockInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 从参数request中获取session
        HttpSession session = request.getSession();
        // 从session中获取用户id
        Object id = session.getAttribute("id");
        if (id == null) { // 用户未登录过
            response.sendRedirect("404.html");
            // 执行拦截,阻止运行
            return false;
        }
    /*    if(id == null){
            return true;
        }*/
        AdminService adminService = (AdminService) SpringUtil.getBean("adminServiceImpl");

        Admin admin = adminService.getById(Integer.valueOf(id.toString()));
        Integer isLock = admin.getIsLock();
        // 判断锁定状态
        if (isLock == 0) { // 用户锁定
            log.info("用户被锁定,将被重定向到登录页面");
            response.sendRedirect("/login");
            // 执行拦截,阻止运行
            return false;
        }
        // 未锁定,直接放行
        return true;
    }
}

  c、基于角色访问

if(SecurityUtils.getSubject().hasRole("角色ID")){
    //具有该角色ID可以操作的,缺陷系统可扩展性差
}

  d、基于资源访问

if(SecurityUtils.getSubject().isPermitted("权限标识")){
    //具有该权限可以操作,可扩展性强
}

  e、控制器使用

Subject subject = SecurityUtils.getSubject();
Object object = subject.getPrincipal();
Session session = subject.getSession();
Object o = session.getAttribute(CookieConstant.SESSION_USER_INFO);//此处获取在认证存的用户session
PrincipalCollection principalCollection = subject.getPreviousPrincipals();
PrincipalCollection principals = subject.getPrincipals();

 

三、流程图(暂时推荐官网)

posted on 2020-05-27 11:12  fuanfei  阅读(241)  评论(0)    收藏  举报