Interceptor与Shiro小结

Interceptor与Shiro

什么是Interceptor

依赖于web框架(在SpringMVC中就是依赖于SpringMVC框架,在Struts2中就是依赖Struts2框架)。在实现上,基于Java的反射机制或者是基于JDK实现的动态代理,属于面向切面编程(AOP)的一种运用。通俗来说,就是提供了一种机制可以使开发者在一个Action执行的前后执行一段代码,也可以在一个Action执行前阻止其执行。

Interceptor实现原理

Java的反射机制(反射的详细内容参考博文)实现了动态代理。

什么是代理和动态代理?代理模式为其它对象提供一种代理,以控制对某个对象的访问。动态代理是指客户通过代理类来调用其它对象的方法。

使用Java的反射机制创建动态代理对象,让代理对象在调用目标方法之前和之后分别做一些事情,然后动态代理对象决定是否调用以及何时来调用被代理对象的方法。这样的动态代理的应用即形成了拦截器(Interceptor)。

Interceptor在SSM中的使用
  • 有两个步骤:
    1.实现HandlerInterceptor接口和方法
    2.将拦截器添加到MVC中。

  • 创建一个类MyInterceptor,实现HandlerInterceptor接口并重写方法

public class MyInterceptor implements HandlerInterceptor {

    //请求发送到Controller之前调用
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {

        /**
         * 配置跨域
         */
        httpServletResponse.setHeader("Access-Control-Allow-Origin","*");//允许所有域名访问
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "*");
        httpServletResponse.setHeader("Access-Control-Max-Age", "3600");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, token");
        httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");

        /**
         * 根据自己的业务编写代买
         * 以验证请求头中的token为例
         */
        if(httpServletRequest.getHeader("token") == null){
            httpServletResponse.setCharacterEncoding("UTF-8");
            httpServletResponse.setContentType("application/json; charset=utf-8");

            HashMap<String,String> returnJson = new HashMap<>();
            returnJson.put("status","415");
            returnJson.put("message","拒绝");
            returnJson.put("data",null);

            PrintWriter out;
            out = httpServletResponse.getWriter();
            out.append(returnJson.toString());

            return false;
        }

        String token = httpServletRequest.getHeader("token");

        if(token.equals("test")){
            return true;
        }

        return false;
    }

    //请求发送到Controller之后调用
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    //完成请求的处理的回调方法
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}
  • MyInterceptor配置到SpringMVC中
    在Spring的MVC配置中加入<mvc:interceptors>标签,控制拦截的范围以及注入拦截器

    <!-- 配置拦截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.cloneZjrt.util.interceptor.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
    
什么是Shiro

Shiro是Java的一个安全框架。因为它相当简单,对比 Spring Security,即使功能上没有 Spring Security强大,但是在实际工作时,Shiro已经能满足大部分情况。

Shiro核心组件

在这里插入图片描述

  • Subject:主体,即当前操作用户
  • SecurityManager:安全管理器,它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务
  • Realm:场所,这里指Shiro获取应用安全数据的方法。当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息(即我们将查询接口在Realm模块中调用,查询到的数据会成为Shiro是否认证,是否授权的依据)。
  • Authenticator:认证器,AuthenticationStrategy如果存在多个realm,则按着具体的策略进行登录控制,例如:如果有一个realm成功即可登录、必须所有realm都成功才能登录等
  • Authorizer:授权器,决定subject能拥有什么样角色或者权限。
  • SessionManager:session管理器,创建和管理用户session。通过设置这个管理器,shiro可以在任何环境下使用session。
  • CacheManager:缓存管理器,可以减少不必要的后台访问。提高应用效率,增加用户体验。
  • Cryptography:Shiro的api大幅度简化java的api中繁琐的密码加密。
Shiro的主要功能

1.Authenticator(认证过程)
在这里插入图片描述

  • Subject(主体)请求认证,调用subject.login(token)
  • SecurityManager (安全管理器)执行认证
  • SecurityManager通过ModularRealmAuthenticator进行认证。
  • ModularRealmAuthenticator将token传给realm,realm根据token中用户信息从数据库查询用户信息(包括身份和凭证)
    realm如果查询不到用户给ModularRealmAuthenticator返回null,ModularRealmAuthenticator抛出异常(用户不存在)
  • realm如果查询到用户给ModularRealmAuthenticator返回AuthenticationInfo(认证信息)
  • ModularRealmAuthenticator拿着AuthenticationInfo(认证信息)去进行凭证(密码)比对。如果一致则认证通过,如果不致抛出异常(凭证错误)。

2.Authorizer(授权过程)
在这里插入图片描述

  • 调用方法isPermitted("")或者hasRole(""),对subject进行授权
  • SecurityManager执行授权,通过ModularRealmAuthorizer执行授权
  • ModularRealmAuthorizer执行realm(自定义的CustomRealm)从数据库查询权限数据调用realm的授权方法:doGetAuthorizationInfo
  • realm从数据库查询权限数据,返回ModularRealmAuthorizer
  • ModularRealmAuthorizer调用PermissionResolver进行权限串比对
  • 如果比对后,isPermitted中"permission串"在realm查询到权限数据中,说明用户访问permission串有权限,否则没有权限,抛出异常。

3.sessionManager:Shiro默认是从cookie中读取sessionId以此来维持会话,也可以自定义sessionManager,继承DefaultWebSessionManager类,重写getSessionId方法。如果在分布式集群环境中可以把session放在redis管理,可以实现session共享。

4.cacheManager:Shiro默认整合了EhCache,来实现缓存,可以自定义cacheManager。如果在分布式集群环境中可以把session放在redis管理,可以实现cache共享。

5.rememeberMeManager:用户登陆选择“自动登陆”本次登陆成功会向cookie写身份信息,下次登陆从cookie中取出身份信息实现自动登陆。如果使用了UserFilter(例如:/user*=user),又如果设置记住我,下次访问这些url时可以不用登陆。

6.常用注解

@RequiresAuthentication
表示当前 Subject 已经通过 login 进行了身份验证;即 Subject. isAuthenticated()返回 true
@RequiresUser
表示当前 Subject 已经身份验证或者通过记住我登录的。
@RequiresGuest
表示当前 Subject 没有身份验证或通过记住我登录过,即是游客身份
@RequiresRoles(value={“admin”, “user”}, logical= Logical.AND)
表示当前 Subject 需要角色 admin 和 user。
@RequiresPermissions (value={“user:a”, “user:b”}, logical= Logical.OR)
表示当前 Subject 需要权限 user:a 或 user:b

SSM整合Shiro
  • 添加依赖
<!-- shiro  -->
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-all</artifactId>
  <version>1.3.2</version>
</dependency>
<!-- 引入ehcache的依赖,给shiro做缓存权限用的 -->
<dependency>
  <groupId>net.sf.ehcache</groupId>
  <artifactId>ehcache</artifactId>
  <version>2.10.6</version>
</dependency>
  • web.xml中添加过滤器(Filter
<!--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>
  • 配置spring-shiro.xml
<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">


    <!-- 定义shiro安全管理器,并配置需要实现的功能-->
    <bean id="securityManager" class="org.apache.shiro.mgt.DefaultSecurityManager">
        <!--实现realm功能-->
        <property name="realm"   ref="realm"/>
        <!--实现cacheManager功能-->
        <property name="cacheManager" ref="cacheManager"/>
        <!--实现seeionManager功能-->
        <property name="sessionManager" ref="sessionManager"/>
        <!--实现记住我功能-->
        <property name="rememberMeManager"  ref="rememberMeManager"/>
    </bean>


    <!-- 定义自己实现的realm域,并配置凭证匹配器-->
    <bean id="realm" class="com.cloneZjrt.util.MyRealm">
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
    </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="/index"/>
        <property name="unauthorizedUrl" value="/unauth"/>
        <property name="filterChainDefinitions">
            <value>
                <!--anon表示无需验证,authc表示需要验证-->
                /login = anon
                /sublogin = anon
                /* = authc
            </value>
        </property>



    </bean>

    <!--配置logout登出管理,id只能为logout,并且在shiro拦截器中需要定义lgout-->
    <bean id="logout" class="org.apache.shiro.web.filter.authc.LogoutFilter">
        <property name="redirectUrl" value="/login"/>
    </bean>

    <!--实现cacha缓存,读取ehcache配置文件-->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:config/ehcache.xml"/>
    </bean>

    <!--配置session管理器-->
    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <property name="globalSessionTimeout" value="300000"/>
        <property name="deleteInvalidSessions" value="true"/>
    </bean>

    <!--设置记住我-->
    <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
        <property name="cookie" ref="remeberMeCookies"/>
    </bean>

    <!-- cookis配置-->
    <bean id="remeberMeCookies"  class="org.apache.shiro.web.servlet.SimpleCookie">
        <!--设置最大存活时间和cookie名称-->
        <property name="maxAge" value="604800"/>
        <property name="name" value="remeberMe"/>
    </bean>

    <!--开启shiro权限注解功能,并配置securityManager属性-->
    <aop:config proxy-target-class="true"></aop:config>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>


    <!--定义凭证匹配器,也就是对密码进行算法加密和次数-->
    <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        <property name="hashAlgorithmName" value="md5"/>
        <property name="hashIterations" value="2"/>
    </bean>

    <!--配置ahtuc过滤器(表单域名称),在页面中账号和密码的name属性的值必须和下面定义的相同-->
    <bean id ="authc" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
        <property name="usernameParam" value="username"/>
        <property name="passwordParam" value="password"/>
        <property name="rememberMeParam" value="remeberMe"/>
    </bean>


    <!--异常处理-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings" >
            <props>
                <!--认证异常和授权异常 -->
                <prop key="org.apache.shiro.authz.UnauthenticatedException">login</prop>
                <prop key="org.apache.shiro.authz.UnauthorizedException">refuse</prop>
            </props>
        </property>

    </bean>
</beans>
  • ApplicationContext.xml中加入spring-shiro.xml
<import resource="classpath*:/spring/spring-shiro.xml"/>
  • 新建一个类MyRealm继承AuthorizingRealm,并实现其中的doGetAuthorizationInfodoGetAuthenticationInfo方法
public class MyRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    /**
     *  获取当前用户的密码
     */
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String name = (String) authenticationToken.getPrincipal();
        String password = getPassword(name);
        if (password == null) {
            return null;
        }
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, password, "MyRealm");
        simpleAuthenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(name));
        return simpleAuthenticationInfo;
    }

    private String getPassword(String name) {
        String password = userService.getPasswordByName(name);
        return password;
    }

    /**
     * 获取当前用户的权限集
     */
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String name = (String) principalCollection.getPrimaryPrincipal();
        Set<String> roles = getRoleByName(name);
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.setRoles(roles);
        return simpleAuthorizationInfo;
    }

    private Set<String> getRoleByName(String name) {
        Set<String> set = userService.getRoles(name);
        return set;
    }
    
}
  • Controller层模拟功能
@PostMapping(value = "/login")
    public String login(UserEntity user){
        UsernamePasswordToken token = new UsernamePasswordToken(user.getName(),user.getPassword());
        Subject subject= SecurityUtils.getSubject();
        try{
            subject.login(token);
        }catch (Exception e){
            return e.getMessage() + "登陆失败";;
        }
    }
@GetMapping(value = "/getRoles")
    public String getRoles(@RequestHeader("token")String token){
        Long userid = JWT.unsign(token,Long.class);
        UserEntity user = userService.queryById(userid);
        UsernamePasswordToken upt = new UsernamePasswordToken(user.getName(),user.getPassword());
        Subject subject= SecurityUtils.getSubject();
        try{
            subject.login(upt);
        }catch (Exception e){
            return e.getMessage();
        }
        List<String> roles = userService.queryUserRoles(userid);
        return roles.toString();
    }

参考博文:Shiro原理简析

SpringMVC拦截器详解

shiro框架详解

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值