Shiro框架入门(代码实现)

2020-05-07  本文已影响0人  摸鱼中的Hy

0. 旨要

用于学习和记录,以方便后期开发和查找。第一次写文章,如有问题,感谢指出!!!

1. Shiro 简单介绍

Shiro是一个功能强大且易于使用的Java安全框架,它执行身份验证,授权,加密和会话管理。
三个核心组件:

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

2. 项目实战

环境:springboot + maven + mybatis
工具:IDEA
注:springboot 项目搭建就不多说。

2.1 依赖包

maven配置

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.1</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<!--    shiro 权限控制-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>

2.2 验证授权配置

@Slf4j
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private SysMenuMapper sysMenuMapper;

    /**
     * 执行授权逻辑
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        SysUserEntity principal = (SysUserEntity) principalCollection.getPrimaryPrincipal();
        Set<String> roleCodeSet = new HashSet<>();//用户角色列表
        Set<String> permsSet = new HashSet<>();//用户权限列表
        SysUserRoleEntity sysUserRolesEntity = sysUserMapper.selectAllRoleMenusByUserId(principal.getId());
        List<SysRoleEntity> roles = sysUserRolesEntity.getSysRoleList();
        for (SysRoleEntity role : roles) {
            roleCodeSet.add(role.getCode());
            if (CollectionUtils.isNotEmpty(role.getSysMenuList())) {
                 role.getSysMenuList().stream().forEach(sysMenu -> permsList.add(sysMenu.getPerms()));
            }
        }
        for (String perm : permsList) {
            if (StringUtils.isNotBlank(perm)) {
                permsSet.addAll(Arrays.asList(perm.trim().split(",")));
            }
        }

        SimpleAuthorizationInfo authorInfo = new SimpleAuthorizationInfo();
        authorInfo.setRoles(roleCodeSet);
        authorInfo.setStringPermissions(permsSet);
        return authorInfo;
    }

    /**
     * 执行认证逻辑,登录时调用
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;

        SysUserEntity user = sysUserMapper.selectByUsername(token.getUsername());
        if (user == null) {
            throw  new UnknownAccountException("账号不存在");
        }
        SimpleAuthenticationInfo authInfo = new SimpleAuthenticationInfo(user, user.getPassword(), ByteSource.Util.bytes(user.getSalt()), getName());
        return authInfo;
    }
}

这里主要要说明下认证时的SimpleAuthenticationInfo对象,一共4个参数,按顺序说明:

  1. 第一个参数为当前用户信息,可以是用户的实体类,也可以是单单的一个用户账号,看个人需求,不过一般都是使用实体类;
  2. 第二个参数为数据库所存储的用户密码,subject.login时用于与token中的密码进行匹配,匹配不上会自动报异常(IncorrectCredentialsException);
  3. 第三个参数为盐,用于加密密码对比。常用的MD5、SHA256等等。注意,使用盐时数据库存储的密码也应是加密后的字符串(可以不设置);
  4. 第四个参数,当前 Realm 名称

2.3 配置

@Configuration
public class ShiroConfig {

    /**
     * 创建 Realm
     */
    @Bean
    public UserRealm myShiroRealm(CredentialsMatcher matcher) {
        UserRealm realm = new UserRealm();
        realm.setCredentialsMatcher(matcher);
        return realm;
    }

    /**
     * 创建安全管理器
     */
    @Bean
    public SecurityManager securityManager(UserRealm realm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        return securityManager;
    }

    /**
     * 配置过滤器
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setSuccessUrl("/index");
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");

        Map<String, String> filterChainMap = new LinkedHashMap<>();
        filterChainMap.put("/webjars/**", "anon");
        filterChainMap.put("/logout", "logout");
        filterChainMap.put("/login", "anon");
        filterChainMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
        return shiroFilterFactoryBean;
    }
    /**
     * 密码加密验证
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName("SHA-256");
        matcher.setHashIterations(16);
        return matcher;
    }

    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * 开启aop注解支持
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}

2.3.1 ShiroFilterFactoryBean 说明

主要说明下loginUrlfilterChainMap

1) loginUrl

没有登录的用户请求需要登录的页面时自动跳转到登录页面,可配置也可不配置,但是不配置的话,会默认查找web下的login.jsp;

2) filterChainMap

Shiro内置过滤器,可以实现权限相关的拦截(url),依照顺序优先匹配,可以使用自定义的filter去覆盖。这里说的只是默认的拦截,没涉及到自定义filter。以下是一些常用的拦截配置说明:

  1. anon: 无需认证
  2. authc: 需要认证才可以访问
  3. user: 如果使用了rememberMe的功能可以直接访问
  4. perms: 该资源必须得到资源权限才可以访问
  5. roles: 该资源必须得到角色权限才可以访问

<font color=red>注:配置时注意别拦截自己的静态资源</font>

2.4 使用

以上1、2、3 步骤已经完成了Shiro的简单配置,也可以使用了

2.4.1 登录

   @PostMapping("/login")
   public String doLogin(String username, String password, Model model) {
       //1. 封装用户数据
       UsernamePasswordToken token = new UsernamePasswordToken(username, password);
       //2. 获取Subject
       Subject subject = SecurityUtils.getSubject();
       //3. 执行登录操作
       try {
           subject.login(token);
       } catch (IncorrectCredentialsException ice) {
           model.addAttribute("msg", "密码不正确");
           return "login";
       } catch (UnknownAccountException uae) {
           model.addAttribute("msg", "账号不存在");
           return "login";
       } catch (AuthenticationException ae) {
           model.addAttribute("msg", "状态不正常");
           return "login";
       }
       model.addAttribute("username", username);
       return "index";
   }

2.4.2 控制器访问权限控制

    @RequestMapping("/info")
    @RequiresPermissions("sys:user:info")//需要在Shiro配置文件里配置AOP才能生效
    public R getUserInfo(Long userId){
        ...
        return R;
    }

2.4.3 Shiro获取用户主体

SecurityUtils.getSubject().getPrincipal();
上一篇 下一篇

猜你喜欢

热点阅读