shiro 框架安装与配置

2020-02-18 21:33:10

安装方式可以使用依赖管理器也可以直接下载jar文件,本文仅使用最方便,最常用,最小化的方式。最新版本是1.5.0,这里使用次新版本(1.4.2)为例。
Shiro 对应用类型没有要求,权限数据可以直接通过配置文件存储,也可以通过实现对应的接口自行处理数据获取。这里只介绍最常用的方式。
官方参考说明
官方参考实例

安装

使用maven安装依赖

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

定义结构

定义一个用于存储用户权限信息的类

@Data
public class CnUser {

    private Integer id;

    private String userName;

    private String nickName;

    private Set<String> roles;

    private Set<String> perms;

}

实现 AuthorizingRealm 类

public class CnRealm extends AuthorizingRealm {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    AuthUserService authUserService;
    @Autowired
    AuthRoleService roleService;

    //执行认证逻辑(登录时调用)
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        String username = userToken.getUsername();
        String password = String.valueOf(userToken.getPassword());
        AuthUser authUser = authUserService.getOne(new QueryWrapper<AuthUser>().eq(AuthUser.USER_NAME,username));
        logger.warn("认证:{}", authUser);
        //用户不存在
        if (authUser == null) {
            return null;
        }

        //帐户已经锁定
        if (authUser.getIsEnable() != 1) {
            throw new LockedAccountException();
        }

        CnUser shiroUser = BeanUtil.toBean(authUser, CnUser.class);
        shiroUser.setRoles(roleService.getRoles(shiroUser.getId()));

        logger.info("shiroUser:{}", shiroUser);
         //密码错误会自动抛异常
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(shiroUser, authUser.getPassword(), getName());
        return info;
    }

    //执行授权(验证权限时调用)
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        CnUser shiroUser = CnUserUtils.getUserInfo();
        if (shiroUser == null) {
            logger.error("userInfo is null");
            return null;
        }
        logger.info("授权:{}", shiroUser);

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //这里只验证角色,如果需要权限继续配置就可以
        info.setRoles(shiroUser.getRoles());

        return info;
    }

}

Shiro 配置类

这里是 Shiro 配置的核心,在统一的配置类中配置


@Configuration
public class ShiroConfig {


    //创建ShiroFilterFactoryBean
    @Bean(name = "shiroFilterFactoryBean")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager webSecurityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(webSecurityManager);

        //添加shiro内置过滤器
        /*
         * anon:表示可以匿名使用。
           authc:表示需要认证(登录)才能使用,没有参数
           roles:参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。
           perms:参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
           rest:根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。
           port:当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数。
           authcBasic:没有参数表示httpBasic认证
           ssl:表示安全的url请求,协议为https
           user:当登入操作时不做检查
         */
        Map<String, String> fMap = new LinkedHashMap<String, String>();

        //资源页面
        fMap.put("/css/**", "anon");
        fMap.put("/img/**", "anon");
        fMap.put("/fonts/**", "anon");
        fMap.put("/js/**", "anon");
        fMap.put("/lib/**", "anon");
        fMap.put("/assets/**", "anon");

        //登陆页
        fMap.put("/login", "anon");

        //公共页
        fMap.put("/", "anon");
        fMap.put("/index", "anon");
        fMap.put("/error", "anon");
        fMap.put("/unauthorized", "anon");

        //注销
        fMap.put("/logout", "logout");

        //管理端
        fMap.put("/admin/**", "authc");

        //个人主页

        fMap.put("/**", "anon");

        //被拦截返回登录页面
        shiroFilterFactoryBean.setLoginUrl("/login");
        //授权拦截返回页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/login");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(fMap);
        return shiroFilterFactoryBean;
    }


    @Bean(name = "defaultWebSecurityManager")
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") CnRealm userRealm) {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(userRealm);
        return defaultWebSecurityManager;

    }

    //创建Realm
    @Bean(name="userRealm")
    public CnRealm userRealm() {
        return new CnRealm();
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager securityManager) {
//        上面配置的 DefaultAdvisorAutoProxyCreator 相当于一个切面,下面这个类就相当于切点了,两个一起才能实现注解权限控制。
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }


}

freemarker 权限标签

Shiro 官方只提供了 JSP 标签,并未提供 freemarker,需要自行开发标签,或采用开源项目。这里使用一个开源标签库

安装

<dependency>
            <groupId>net.mingsoft</groupId>
            <artifactId>shiro-freemarker-tags</artifactId>
            <version>1.0.1</version>
            <exclusions>
                <exclusion>
                    <artifactId>shiro-core</artifactId>
                    <groupId>org.apache.shiro</groupId>
                </exclusion>
            </exclusions>
        </dependency>

配置

然后在配置类中进行 freemarker 配置,这个配置中同时支持了 jdk8 的时间类型。


@Configuration
public class FreeMarkerConfig {

    @Autowired
    private freemarker.template.Configuration configuration;
    @PostConstruct
    public void setSharedVariable() {
        try {
            configuration.setSharedVariable("shiro", new ShiroTags());
            configuration.setObjectWrapper(new CustomObjectWrapper());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static class CustomObjectWrapper extends DefaultObjectWrapper {
        @Override
        public TemplateModel wrap(Object obj) throws TemplateModelException {
            if (obj instanceof LocalDateTime) {
                Timestamp timestamp = Timestamp.valueOf((LocalDateTime) obj);
                return new SimpleDate(timestamp);
            }
            if (obj instanceof LocalDate) {
                Date date = Date.valueOf((LocalDate) obj);
                return new SimpleDate(date);
            }
            if (obj instanceof LocalTime) {
                Time time = Time.valueOf((LocalTime) obj);
                return new SimpleDate(time);
            }
            return super.wrap(obj);
        }
    }
}

标签说明

权限标签如下:

  1. guest(游客)
<@shiro.guest>  
    您当前是游客,<a href="javascript:void(0);">登录</a>
</@shiro.guest> 
  1. user(已经登录,或者记住我登录)
<@shiro.user>  
    欢迎[<@shiro.principal/>]登录,<a href="/logout.shtml">退出</a>  
</@shiro.user> 
  1. authenticated(已经认证,排除记住我登录的)
<@shiro.authenticated>  
    用户[<@shiro.principal/>]已身份验证通过  
</@shiro.authenticated> 
  1. notAuthenticated(和authenticated相反)
    该功能主要用途:识别是不是本次操作登录过的,比如支付系统,进入系统可以用记住我的登录信息,但是当要关键操作的时候,需要进行认证识别。
<@shiro.notAuthenticated>
    当前身份未认证(包括记住我登录的)
</@shiro.notAuthenticated> 
  1. principal标签
    principal标签,取值取的是你登录的时候。在Realm实现类中的如下代码:
    return new SimpleAuthenticationInfo(user,user.getPswd(), getName());
    在 new SimpleAuthenticationInfo(第一个参数,....) 的第一个参数放的如果是一个username,那么就可以直接用。
    <!--取到username--> <@shiro. principal/>
    如果第一个参数放的是对象,比如放User对象。那么如果要取username字段。
    <!--需要指定property--> <@shiro.principal property="username"/>
    和Java如下Java代码一致
User user = (User) SecurityUtils.getSubject().getPrincipals();
String username = user.getUsername();
  1. hasRole标签(判断是否拥有这个角色)
<@shiro.hasRole name="admin">  
    用户[<@shiro.principal/>]拥有角色admin<br/>  
</@shiro.hasRole>   
  1. hasAnyRoles标签(判断是否拥有这些角色的其中一个)
<@shiro.hasAnyRoles name="admin,user,member">  
    用户[<@shiro.principal/>]拥有角色admin或user或member<br/>  
</@shiro.hasAnyRoles>   
  1. lacksRole标签(判断是否不拥有这个角色)
<@shiro.lacksRole name="admin">  
    用户[<@shiro.principal/>]不拥有admin角色
</@shiro.lacksRole>   
  1. hasPermission标签(判断是否有拥有这个权限)
<@shiro.hasPermission name="user/add">  
    用户[<@shiro.principal/>]拥有user/add权限
</@shiro.hasPermission>   
  1. lacksPermission标签(判断是否没有这个权限)
<@shiro.lacksPermission name="user/add">  
    用户[<@shiro.principal/>]不拥有user/add权限
</@shiro.lacksPermission> 
Copyright tg-blog 京ICP备15066502号-2