Shiro使用总结

背景:本人刚刚把Shiro整合在了SSM架构的项目上,maven做项目管理工具,实现了简单的认证和授权,以及记住我和验证码功能,简单的记录一下;

需要ssm项目demo的可以看我之前的文章:最简单易懂的Idea搭建SSM项目过程和配置(文末有demo哦)

寄语:代码改变世界不敢说,活在当下,敲好每一行代码就可以了

一、引入Shiro依赖

我用的Shiro版本是1.2.5,刚开始是引入shiro-all的,后来看到官网上强烈建议不要引入shiro-all,而是要按需引入,我觉得相当有道理哇,于是就引入了下面三个依赖。

    <properties>
        <shiro.version>1.2.5</shiro.version>
      </properties>

    <!-- shiro -->
    <!-- shiro核心包 -->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-core</artifactId>
      <version>${shiro.version}</version>
    </dependency>
    <!-- 添加shiro web支持 -->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-web</artifactId>
      <version>${shiro.version}</version>
    </dependency>
    <!-- 添加shiro spring整合 -->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>${shiro.version}</version>
    </dependency>

二、在web.xml中添加Shiro过滤器

此处过滤所有请求,所以filter-mapping中url-pattern设置的是/*

    <!--shiro配置-->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

三、配置sping-shiro.xml文件

在spring的配置文件中引入spring-shiro.xml

    <!--引入spring-shiro-->
    <import resource="spring-shiro.xml"/>

Shiro配置说明

Apache Shiro的配置主要分为四部分:

  • 对象和属性的定义与配置
  • URL的过滤器配置
  • 静态用户配置
  • 静态角色配置
配置Shiro对象

主要是为了自定义各个Shiro组件,项目比较简单,我只自定义了Relam和记住我功能

    <!--创建shiro的安全管理器的对象-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="myShiroRealm" />
        <!--声明会话管理器属性-->
        <!--<property name="sessionManager" ref="sessionManager"></property>-->
        <!--声明rememberMe-->
        <property name="rememberMeManager" ref="rememberMeManager"></property>
    </bean>

自定义Relam和记住我

    <!-- 项目自定义的Realm -->
    <bean id="myShiroRealm" class="com.blueice.shiro.MyShiroRealm">
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
        <property name="cachingEnabled" value="false"/>
    </bean>
    <!--记住我的配置-->
    <!--声明cookie对象-->
    <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        <constructor-arg value="rememberMe"></constructor-arg>
        <!--只有http的链接才能使用cookie-->
        <property name="httpOnly" value="true"></property>
        <!--cookie的失效时间30天,单位是秒-->
        <property name="maxAge" value="2592000"></property>
    </bean>
    <!--声明记住我的管理器对象-->
    <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
        <property name="cookie" ref="rememberMeCookie"></property>
    </bean>

配置Shiro过滤器

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="/login" />
        <property name="successUrl" value="/welcome" />
        <property name="unauthorizedUrl" value="/login" />
        <property name="filters">
            <util:map>
                <entry key="authc" value-ref="captchaFormAuthenticationFilter"/>
            </util:map>
        </property>
        <property name="filterChainDefinitions">
            <value>
                /static/**  = anon
                /plugins/** = anon
                /kaptcha    = anon
                /kaptcha/get= anon
                /login*     = anon
                /logout     = logout
                /**         = authc
                /**         = user
            </value>
        </property>
    </bean>

本人项目中引入了谷歌开源框架Kaptcha做验证码校验,因此自定义了一个authc的过滤器captchaFormAuthenticationFilter,重写了创建token的方式,加入了验证码字段。

<!--自定义添加验证码的过滤器-->
<bean id="captchaFormAuthenticationFilter" class="com.blueice.filter.MyFormAuthenticationFilter"/>

URL表达式说明

1、URL目录是基于HttpServletRequest.getContextPath()此目录设置

2、URL可使用通配符,**代表任意子目录

3、Shiro验证URL时,URL匹配成功便不再继续匹配查找。所以要注意配置文件中的URL顺序,尤其在使用通配符时。

Filter Chain定义说明

1、一个URL可以配置多个Filter,使用逗号分隔

2、当设置多个过滤器时,全部验证通过,才视为通过

3、部分过滤器可指定参数,如perms,roles

Shiro内置的FilterChain

Filter Name 功能
anno 不需要授权、登录就可以访问。eg:/login
authc **需要登录授权才能访问。
authcBasic Basic HTTP身份验证拦截器
logout 退出拦截器。退出成功后,会 redirect到设置的/URI
noSessionCreation 不创建会话连接器
perms 授权拦截器:perm['user:create']
port 端口拦截器.eg:port[80]
rest rest风格拦截器
roles 角色拦截器。eg:role[administrator]
ssl ssl拦截器。通过https协议才能通过
user 用户拦截器。eg:登录后(authc),第二次没登陆但是有记住我(remember)都可以访问。

四、登录认证

收集token信息

/*先把账号密码传入shiro里面的UsernamePasswordToken对象里面*/
CaptchaToken token = new CaptchaToken(username, pwd, captchaCode, rememberMe);

提交token信息并处理

    /*创建Subject对象*/
    Subject subject = SecurityUtils.getSubject();

    /*调用subject.login()进行登录*/
    try {
        subject.login(token);
    } catch (UnknownAccountException e) {
        error = "用户名/密码错误!";
    } catch (IncorrectCredentialsException e) {
        error = "用户名/密码错误!";
    } catch (ExcessiveAttemptsException e) {
        error = "登录失败多次,账户锁定10分钟!";
    } catch (CaptchaException e) {
        error = "验证码输入有误!";
    } catch (AuthenticationException e) {
        /*其他错误,比如锁定,如果想单独处理请单独catch处理*/
        System.out.println(e.getMessage());
        error = "未知异常,请联系管理员处理!";
    }

五、注销用户

logout()会删除session中的所有信息,完成用户的注销操作。

    Session session = SecurityUtils.getSubject().getSession();
    Subject subject = SecurityUtils.getSubject();
    subject.logout();

六、授权实现

本文中通过注解 @RequiresPermissions实现授权

    @RequestMapping("/test")
    @ResponseBody
    @RequiresPermissions("user:role:tes")
    public String testMap() {
        Map<String, String> map = indexService.testMap();
        return map.toString();
    }

七、自定义Relam

Relam是Shiro的三大核心(SubjectSecurityManagerRelam)之一,通过集成AuthorizingRelam实现自定义Relam,重写了授权信息的doGetAuthorizationInfo方法和认证信息的doGetAuthenticationInfo方法。

    public class MyShiroRealm extends AuthorizingRealm {

    }

认证实现

Relam会根据上文login()方法提供的token进行认证

     @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username = token.getUsername();

        SysUser user = null;
        if (username != null  && !"".equals(username)) {
            user = indexService.selectUserInfoByUsername(username);
        }
        if (null == user) {
            /*没找到帐号*/
            throw new UnknownAccountException("用户信息不存在!");
        }
        if (user.getStatus() == 0) {
            /*用户被停用*/
            throw new LockedAccountException("用户信息已被停用");
        }

        ByteSource salt = ByteSource.Util.bytes(user.getUsername());
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(token.getPrincipal(), user.getPassword(),
                 salt, getName());
        return info;
    }

授权实现

获取数据库中当前登录用户的权限列表,跟@RequiresPermissions的进行比对,看用户是否有此授权,此处为了方便起见,直接设置了权限为"user:role:test",更多权限一般用逗号分隔。

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String userName = (String)principals.getPrimaryPrincipal();
        /*用户权限列表*/
        Set<String> permsSet = new HashSet<>();
        permsSet.add("user:role:test");

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setStringPermissions(permsSet);

        return authorizationInfo;
    }

结语

我是通过Md2All进行排版的,但是代码中//注释会读不到换行,导致后面全部被当成注释,所以我换成了/**/注释;
Md2All中的代码样式,如果是黑色背景,到了博客园都会变得很难看,最后我选了个白色背景的!
以上是我此次Shiro使用的总结,互相学习,多多交流哈!

原文地址:https://www.cnblogs.com/steveshao/p/13645417.html