Shiro集成到Spring

为什么要集成Spring?

我们的项目基本都是通过Spring来管理bean的,如果要想使用Shiro,那就要把shiro集成到Spring。集成Spring的核心就是把框架的核心类(SecurityManager,Subject,Realm)交给Spring管理

集成

第一步- -在自己的项目中导入相关的jar包

<!-- shiro的支持包 -->
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-all</artifactId>
  <version>1.4.0</version>
  <type>pom</type>
</dependency>
<!-- shiro与Spring的集成包 -->
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-spring</artifactId>
  <version>1.4.0</version>
</dependency>

第二步- -配置文件

在web.xml中配置shiroFilter

Spring与shiro集成:需要定义一个shiro过滤器(这是一个代理过滤器,它会到spring的配置中找一个名称相同的真实过滤器)

<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>

配置applicationContext-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"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <!--spring管理shiro的核心对象-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="itsourceRealm"/>
    </bean>


    <!--配置自定义realm-->
    <bean id="itsourceRealm" class="cn.itsource.aisell.realm.ItsourceRealm">
        <!--密码匹配器-->
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <property name="hashAlgorithmName" value="MD5"/>
                <property name="hashIterations" value="10"/>
            </bean>
        </property>
    </bean>

    <!--
        shiro的核心bean
        注意: 该bean的id必须要和web.xml中代理过滤器的名字一致,否则要报错

    -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <!--如果你没有认证通过,直接跳到loginUrl对应的页面-->
        <property name="loginUrl" value="/login/login.jsp"/>
        <!--登录成功之后,跳转的指定的界面-->
        <property name="successUrl" value="/login/success.jsp"/>

       <!-- 如果没有对应的权限则跳到unauthorizedUrl对应的界面-->
        <property name="unauthorizedUrl" value="/login/unauthorized.jsp"/>
       <!-- 配置过滤器链-->
        <!--
        这种方式真实开发不用,因为数据写死了的,灵活性较差
         <property name="filterChainDefinitions">
             <value>
                     anon:匿名过滤器,直接放行
                     authc:必须认证通过之后,才能放行
                     logout:登出
                 /s/login.jsp = anon
                 /login=anon
                 /logout=logout
                 /employee/index=perms["employee:*"]
                 /dept/index=perms["dept:index"]  访问/dept/index必须要拥有dept:index权限
                /** = authc
            </value>
        </property>
        -->
       <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"/>
        <!--自定义过滤器权限-->
        <property name="filters">
            <map>
                <entry key="itsourceFilter">
                    <bean class="cn.itsource.aisell.realm.ItsourcePermissionsAuthorizationFilter"/>
                </entry>
            </map>
        </property>
    </bean>
    <bean id="chainDefinitionMap" class="cn.itsource.aisell.realm.ItsourceFilterChainDefinitionMap"/>
    <bean id="filterChainDefinitionMap" factory-bean="chainDefinitionMap" factory-method="createMap"/>

</beans>

在applicationContext.xml中引入shiro文件

<!-- 引入shiro.xml文件-->
<import resource="classpath:applicationContext-shiro.xml"/>

在配置文件中我们自定义了Realm过滤器权限 还有配置过滤器链
因为现有的Realm都不能满足我们的需求 而这种情况下,就需要我们自己定义一个Realm来实现相应的功能
自定义Realm一般直接继承AuthorizingRealm接口即可(里面包含身份认证与授权两个方法)
什么是Realm?

Realm:域,Shiro 从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色/权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource , 即 安 全 数 据 源 。(摘自《跟我学Shiro》

自定义Realm
ItsourceRealm

import cn.itsource.aisell.domain.Employee;
import cn.itsource.aisell.service.IEmployeeService;
import cn.itsource.aisell.service.IPermissionService;
import cn.itsource.aisell.util.Md5Util;
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 ItsourceRealm extends AuthorizingRealm {

    @Autowired
    private IEmployeeService employeeService;

    @Autowired
    private IPermissionService permissionService;

    /**
     * 授权方法
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Set<String> permissions = permissionService.findPermissionsByUserLogin();
        info.setStringPermissions(permissions);
        return info;
        }

    /**
     * 认证方法
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //获取用户名
        String principal = (String) token.getPrincipal();
        //根据用户名查找用户
        Employee employee = employeeService.findByUsername(principal);

        if (employee == null) {
            return null;
        }

        ByteSource salt = ByteSource.Util.bytes(Md5Util.SALT);

        //当前的realm名  getName()
        return new SimpleAuthenticationInfo(employee,employee.getPassword(),salt,getName());
    }

}

自定义过滤器链

ItsourceFilterChainDefinitionMap.java

public class ItsourceFilterChainDefinitionMap {

    @Autowired
    private IPermissionService permissionService;

    public Map<String,String> createMap(){
        Map<String, String> map = new LinkedHashMap<>();
        map.put("/login/login.jsp", "anon");
        map.put("/login", "anon");
        map.put("/login/**", "anon");
        map.put("/forget/forget", "anon");
        map.put("/static/**", "anon");
        map.put("/*.jsp", "anon");
        map.put("/logout", "logout");

        List<Permission> all = permissionService.findAll();
        for (Permission permission : all) {
            map.put(permission.getUrl(),"itsourceFilter["+permission.getSn()+"]");
        }

        map.put("/**", "authc");
        /*
        anon:匿名过滤器,直接放行
        authc:必须认证通过之后,才能放行
        logout:登出
        */
        return map;
    }
}

Authentication 身份认证

LoginController.java


@Controller
public class LoginController {
 
    @RequestMapping("/login")
    @ResponseBody
    public String login(String username,String password){
        Subject currentUser = SecurityUtils.getSubject();  //拿到当前用户
        if(!currentUser.isAuthenticated()){ //如果这个用户没有登录,则进行登录认证
            try {
                UsernamePasswordToken token = new UsernamePasswordToken(username,password);
                //调用login方法时底层会去调用realm中的认证方法
                currentUser.login(token);
                return new AjaxResult();
            }catch (UnknownAccountException e) {
                e.printStackTrace();
                return new AjaxResult(false, "账号不存在");
            }catch (IncorrectCredentialsException e) {
                e.printStackTrace();
                return new AjaxResult(false, "密码错误");
            } catch (AuthenticationException e) {
                e.printStackTrace();
                //当出现这个错误时候 就说明是代码哪里错了
                return new AjaxResult(false, "网络繁忙请稍后再试");
            }
        }
    }
 
    @RequestMapping("/logout")
    public String logout(){
        Subject currentUser = SecurityUtils.getSubject();
        currentUser.logout();
        return new AjaxResult(true, "注销成功");
    }
}
发布了60 篇原创文章 · 获赞 9 · 访问量 1975

猜你喜欢

转载自blog.csdn.net/qq_40629521/article/details/104084814
今日推荐