Spring和Shiro的简单整合

Spring 简单整合Shiro

本博文主要是简单记录一下Shiro整合Spring的一些基本配置,并不会过多讲述其中的细节。其实我觉得我自己练手写的破网页直接用拦截器或者过滤器或者更简单粗暴在controller中判断权限就已经足够了,但是这一套规范还是学一下把…建议初学的小伙伴可以学习张涛老师的跟我学“shiro”系列教程

重要概念

1.Authentication:身份认证,例如用户的登陆是否成功
2.Authentization:权限认证,授权方面。例如某一个用户登陆成功了,那么需要直到它具有什么权限
3.Session Manager:会话管理,例如用户的登陆就是一次会话
4.Cryptography:加密
5.Remember Me:一次登陆,下一次登陆就不用登陆了
6.Subject:主体,例如我们常见的主体就是用户,但是它可以不是一个具体的人物。所有的subject都绑定到SecurityManager,和subject的所有交互都会交给SecurityManager
7.SecurityManager:安全管理器,所有有关安全的操作都会和SecurityManager交互,它管理着所有的subject,是shiro的核心部分,可以类比为SpringMVC中的DispatcherServlet
8.Realm:域:shiro从Realm中获取安全数据(例如用户角色,权限),即要从SecurityManager验证用户身份,要从Realm获取相应的用户进行比较来确定用户身份是否合法,也需要从Realm中得到用户的相应的用户角色权限进行用户是否能进行操作,可以把Realm看成DataSource,安全数据源;可以有多个Realm
9.Authenticator:认证器,负责主体的认证,程序员可以编写自己的认证器
10.Authentication Strategy:认证策略,即什么情况下才算用户认证通过了
11.Authrizer:授权器,决定主体是否有权限进行相应的操作

一般验证过程

1.从上面的概念可以看出一般shiro应用的验证过程

  • 通过subject进行认证和授权,subject又委托给SecurityManager来处理
  • 我们需要为SecurityManager注入Realm,才能让SecurityManager为我们比较用户信息是否一样

Maven dependency


      <!-- shiro -->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-core</artifactId>
      <version>${shiro.version}</version>
    </dependency>

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

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

Spring 整合 Shiro : Hello-World


1.引入上述Maven依赖.
2.配置web.xml:拦截url(很好理解,既然是为了安全,那我肯定要知道你想访问哪一些资源).

  <!-- shiro filter -->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

3.ApplicationContext.xml配置:将所有的pojo都交给spring为我们管理

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="index.html"/>
        <property name="unauthorizedUrl" value="403.html"/>
        <property name="filterChainDefinitions">
            <value>
                /static/**=anon
                /hello=anon
                /index.html = anon
                /testPerms = perms["user:delete"]
                /testRoles = roles["admin"]
                /testUpdate = roles["root"]
                /* = authc
            </value>
        </property>
    </bean>

    <!-- security Manager -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" p:realm-ref="realm"
          p:sessionManager-ref="sessionManager"
    />

    <!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="staticMethod"
                  value="org.apache.shiro.SecurityUtils.setSecurityManager" />
        <property name="arguments" ref="securityManager" />
    </bean>

    <!-- realm -->
    <bean id = "realm" class="com.dadagum.realm.UserRealm"  p:credentialsMatcher-ref="credentialsMatcher"/>

    <!-- HashedCredentialsMatcher : 指定md5方法 -->
    <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher" p:hashAlgorithmName="md5" p:hashIterations="1"/>

    <!--&lt;!&ndash; 会话ID生成器 &ndash;&gt;-->
    <bean id="sessionIdGenerator"
          class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator" />

    <!-- 会话Cookie模板 关闭浏览器立即失效 -->
    <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        <constructor-arg value="sid" />
        <property name="httpOnly" value="true" />
        <property name="maxAge" value="-1" />
    </bean>

    <!-- 会话DAO -->
    <bean id="sessionDAO"
          class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
        <property name="sessionIdGenerator" ref="sessionIdGenerator" />
    </bean>

    <!-- 会话验证调度器,每30分钟执行一次验证 ,设定会话超时及保存 -->
    <bean name="sessionValidationScheduler"
          class="org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler">
        <property name="interval" value="1800000" />
        <property name="sessionManager" ref="sessionManager" />
    </bean>

    <!-- 会话管理器 -->
    <bean id="sessionManager"
          class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <!-- 全局会话超时时间(单位毫秒),默认30分钟 -->
        <property name="globalSessionTimeout" value="1800000" />
        <property name="deleteInvalidSessions" value="true" />
        <property name="sessionValidationSchedulerEnabled" value="true" />
        <property name="sessionValidationScheduler" ref="sessionValidationScheduler" />
        <property name="sessionDAO" ref="sessionDAO" />
        <property name="sessionIdCookieEnabled" value="true" />
        <property name="sessionIdCookie" ref="sessionIdCookie" />
    </bean>

    <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />


</beans>

4.实现自定义Realm(此例子中就是UserRealm,mapper根据用户名在数据库中取得相应的盐值、密码、角色和权限信息):

package com.dadagum.realm;

import com.dadagum.mapper.UserInfoMapper;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Set;

public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //获取角色角色、授权
        String userName =(String) principalCollection.getPrimaryPrincipal();
        //获取角色和权限
        Set<String> roles = userInfoMapper.getRolesByUserName(userName);
        Set<String> permissions = userInfoMapper.getPermissionsByUserName(userName);
        //返回授权对象
        SimpleAuthorizationInfo s = new SimpleAuthorizationInfo();
        s.setStringPermissions(permissions);
        s.setRoles(roles);
        return s;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 传来的用户名
        String userName= authenticationToken.getPrincipal().toString();
        //获取数据库中的密码,盐
        String password = userInfoMapper.getPasswordByUserName(userName);
        String salt = userInfoMapper.getSaltByUserName(userName);
        System.out.println(password);
        System.out.println(salt);

        //认证信息里存放账号密码, getName() 是当前Realm的继承方法,通常返回当前类名
        return new SimpleAuthenticationInfo(userName, password, ByteSource.Util.bytes(salt),getName());
    }

}

5.登陆测试
①先插入测试数据(先不演示授权部分) :此处的加密方式应该和xml中配置的HashedCredentialsMatcher 配置一样,盐值为随即生成,因此记录在数据库中

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring/spring-dao.xml")
public class MybatisSpringTest {

    @Autowired
    private UserInfoMapper mapper;

    @Test
    public void testMapper(){
        UserInfo userInfo = new UserInfo();
        userInfo.setUserAccount("admin2");
        ByteSource salt = new SecureRandomNumberGenerator().nextBytes();
        SimpleHash simpleHash = new SimpleHash("md5", "admin2", salt.toHex(), 1);
        userInfo.setUserPassword(simpleHash.toString());
        userInfo.setSalt(salt.toHex());
        mapper.addUser(userInfo);
    }
}

②controller测试,启动tomcat后肯定是成功的

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public JsonResult<?> hello(){
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("admin2", "admin2");
        try {
            subject.login(token);
            System.out.println("登陆成功");
        } catch (AuthenticationException e) {
            System.out.println("登陆失败");
            e.printStackTrace();
        }
        return new JsonResult<>("hello world", "200", "ok");
    }

Hello-World程序就到此结束了。另外,配置文件中可以对一个url进行多个角色或者权限的规定,例如/testAll = roles["admin", "root"],那么需要同时具备”admin”和”root”的角色才可以访问,那么如果我想要具有这两个角色其中之一就可以访问呢 ,这个时候roles就不好使了,需要自定义一个过滤器:

①编写继承AuthorizationFilter的类(通过父类获得当前subject,参数Object其实就是例如”admin”,”root”之类的角色定义,这个逻辑也恨简单,如果没有参数,说明没有限制,放行;如果有内容,,判断是否包含列表中的角色)

public class RolesOrFilter extends AuthorizationFilter {
    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
        Subject subject = getSubject(servletRequest, servletResponse);
        String[] roles = (String[]) o;
        if (roles == null || roles.length == 0) return true;
        for (String role : roles)
            if (subject.hasRole(role)) return true;
        return false;
    }
}

②ApplicationContext.xml中加入上述类:<bean id="rolesOrFilter" class="com.dadagum.filter.RolesOrFilter"/>
③shiroFilter增加为:

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="login.html"/>
        <property name="unauthorizedUrl" value="403.html"/>
        <property name="filterChainDefinitions">
            <value>
                /static/**=anon // 静态资源略过
                /login.html = anon
                /loginHandler = anon
                /testPerms = perms["user:delete"]
                /testRoles = roles["admin"]
                /testUpdate = roles["root"]
                /testAll = rolesOr["admin","root"]
                /* = authc
            </value>
        </property>

        <property name="filters">
            <util:map>
                <entry key="rolesOr" value-ref="rolesOrFilter"/>
            </util:map>
        </property>
</bean>

之后发现登录后即使只有admin角色也可以访问/testAll了

基于注解配置访问权限


1.pom.xml加入aspectJ的weaver jar包,然后在ApplicationContext.xml中加入

    <!-- use annotation -->                                                                                                                         
    <aop:config proxy-target-class="true"/>                                                                                                         
    <bean class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>                                                                              
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor" p:securityManager-ref="securityManager"/>        

2.使用注解,表明具有admin角色才可以访问

    @RequiresRoles("admin")
    @RequestMapping(value = "/testRole", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
    @ResponseBody
    public String testRole(){
        return "testRole成功";
    }

session


待续

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值