Shiro框架

Shiro简介

Shiro是Apache提供的一个功能强大且易于使用的Java安全框架,主要用于:认证(Authentication)、鉴权(Authorization)、加密(Cryptography)、会话管理(Session Management)

Shiro核心组件

Shiro运行过程中有三大核心组件:Subject、SecurityManager、Realm

Subject

个体、事务、主体,Subject可以调用SecurityManager的绝大多数方法,相当于是个传话者

SecurityManager

负责调配资源、调度各种各样组件完成业务的实现。SecurityManager内部非常复杂,当我们想要使用SecurityManager实现某些功能时只需要直接调用Shiro提供的简化的方法(将SecurityManager交给SecurityUtils托管,通过SecurityUtils获取Subject去操作SecurityManager,Subject可以调用SecurityManager的绝大多数方法,相当于是个传话者),相当于是个大哥,负责主要的核心业务

Realm

为Shiro业务的实现提供数据:权限数据、角色数据、用户数据;相当于DAO层(持久层)

在这里插入图片描述

RBAC模型

Role Base Access Controller基于角色的访问控制,模型中有三个主体:用户、角色、权限

权限访问控制做的事:
1、身份校验:判断用户是否为合法用户
2、权限校验:用户要做某事、使用某些资源,必须要拥有某角色或某权限,判断用户是否拥有相关的角色权限

配置shiro.ini为realm提供数据支持

[users]
# 一个[]表示一个区域,区域与区域之间以[]识别
# 配置用户信息
# 用户名=密码,角色
root=123,admin
zhangsan=456,userManager

[roles]
# 配置角色信息
# 角色名=权限1,权限2
admin=*
userManager=user:insert,user:query,user:delete,user:update
deptManager=dept:insert,dept:query,dept:delete,dept:update

[main]
# 配置校验失败跳转的地址
# 没有身份认证时,跳转地址(以重定向的方式跳转)
shiro.loginUrl = /user/login/page
# ⻆⾊或权限校验不通过时,跳转地址(以重定向的方式跳转)
shiro.unauthorizedUrl=/author/error
# 登出后的跳转地址,回⾸⻚(以重定向的方式跳转)
shiro.redirectUrl=/user/login/page

[urls]
# 配置访问接口所需要的角色权限
# 接口路径=是否需要认证,roles["admin","user"],perms["user:insert","user:query"]
# anon => 不需要身份认证
# authc => 需要身份认证
user/login=anon
user/insert=authc,roles["admin"]
user/query=authc,prems["user:query"]
    public static void main(String[] args) {
        IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager manager = factory.getInstance();
        SecurityUtils.setSecurityManager(manager);

        Subject subject = SecurityUtils.getSubject();

        UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "456");
        // shiro为什么能判断密码正确与否?
        // 是因为Realm为SecurityManager提供了数据支持,SecurityManager将令牌中的账户秘密跟Realm中的账号和密码做比对
        // 通过IniSecurityManagerFactory这种方式获取到的SecurityManager,会将所有的用户、角色、权限信息都读取到IniRealm中,在校验的时候会将token中的用户名和密码和从Reaml中的用户和名密码进行比对
        // users是一个LinkedHashMap,roles是一个LinkedHashMap
        subject.login(token);

        System.out.println(subject.isAuthenticated());

        System.out.println(subject.hasRole("user:*"));

        subject.login(token);
    }
<!--
  配置web.xml
-->
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">
    <display-name>Archetype Created Web Application</display-name>
    <!--
      初始化SpringMVC的环境,创建DispatcherServlet
      -->
    <servlet>
        <servlet-name>mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:mvc.xml</param-value>
        </init-param>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <!--
         1. 在项目的最外层 构建访问控制层
         2. 在启动时,初始化shiro环境
        -->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!--  在项目启动时,加载web-info 或 classpath下的 shiro.ini ,并构建WebSecurityManager。
         构建所有配置中使用的过滤器链(anon,authc等),ShiroFilter会获取此过滤器链
     -->
    <listener>
        <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
    </listener>

    <!--
      初始化Spring容器的环境
      -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    <filter>
        <filter-name>encoding</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

web.xml配置详解

ShiroFilter原理

在这里插入图片描述
Shior的认证、鉴权是通过一系列的过滤器链完成的,ShiroFilter相当于一层外壳,囊括着所有的过滤器,拦截所有的请求。

过滤器过滤器类描述例子
anonAnonymouseFilter表示不需要身份认证,可以匿名访问user/login=anon
authcFormAuthenticationFilter表示需要登录才能访问user/query=authc
自定义Realm
// shiro在认证的过程中会先调用Realm获取用户信息、角色信息、权限信息,realm会在shiroFilter执行,
public class MyRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("Shiro正在获取角色权限信息");
        String userName = (String) principalCollection.getPrimaryPrincipal();
        RoleServiceImpl roleServiceImpl = ContextLoader.getCurrentWebApplicationContext().getBean("roleServiceImpl", RoleServiceImpl.class);
        PermissionServiceImpl permissionServiceImpl = ContextLoader.getCurrentWebApplicationContext().getBean("permissionServiceImpl", PermissionServiceImpl.class);
        // 获取角色信息
        Set<String> roleNames = roleServiceImpl.getRoleNameByUserName(userName);
        // 获取权限信息
        Set<String> permissionNames = permissionServiceImpl.getPermissionNameByUserName(userName);

        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(roleNames);
        simpleAuthorizationInfo.setStringPermissions(permissionNames);

        return simpleAuthorizationInfo;
    }

    /**
     * 作用:查询身份信息,返回即可,不用做任何比对,交给AuthenticationFilter进行比对
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("Shiro正在获取用户信息");
        String userName = (String) authenticationToken.getPrincipal();
        UserServiceImpl userServiceImpl = ContextLoader.getCurrentWebApplicationContext().getBean("userServiceImpl", UserServiceImpl.class);
        User currentUser = userServiceImpl.getUserByUserName(userName);
        // 到时候AuthenticationFilter会拿simpleAuthenticationInfo里面的password和用户输入的password做比对
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(currentUser.getUserName(), currentUser.getPassword(), this.getName());
        return simpleAuthenticationInfo;
    }
}

在shiro.ini文件的[main]区域添加如下内容,并删除[users]和[roles]两个区域的内容,[users]和[roles]中的数据将由自定义的Realm提供

#将自定义的realm和Shiro绑定,告知Shiro不再使用自定义的Realm
# userRealm是自定义名称
userRealm=com.XXX.XXX.XXX.MyRealm
securityManager.realms=$userRealm
配置密码加密\校验策略

加密存储策略:

@PostMapping("/add")
    public void userAdd(User user){
        String salt = UUID.randomUUID().toString();
        //Sha256加密算法转base64进行存储
        String newPassword1 = new Sha256Hash(user.getPassword(),salt,1000).toBase64();
        //Sha256加密算法转16进制进行存储
        //String newPassword2 = new Sha256Hash(user.getPassword(),salt,1000).toHex();
        //转base64进行存储
        //String newPassword3 = new Md5Hash(user.getPassword(),salt,1000).toBase64();
        //转16进制进行存储
        //String newPassword4 = new Md5Hash(user.getPassword(),salt,1000).toHex();
        user.setSalt(salt);
        user.setPassword(newPassword1);
        userService.addUser();
    }

校验策略:

第一步:在shiro.ini文件的[main]区域添加如下内容:

# 指定密码比对器======================================================
# passwordMatcher是自定义名称
passwordMatcher=org.apache.shiro.authc.credential.SimpleCredentialsMatcher
# 指定加密算法
passwordMatcher.hashAlgorithmName=sha-256
# 指定迭代次数
passwordMatcher.hashIterations=10000
# 指定加密结果的编码方式:true转换为十六进制格式Hex,false转换为Base64格式
passwordMatcher.storedCredentialsHexEncoded=false

# 指定Realm=========================================================
#将自定义的realm和Shiro绑定,告知Shiro不再使用自定义的Realm
# userRealm是自定义名称
userRealm=com.XXX.XXX.XXX.MyRealm
# 通过Realm中告知SecurityMannager使用的密码比对器
userRealm.credentialsMatcher=$passwordMatcher
# 告知SecurityMannager使用的Realm
securityManager.realms=$userRealm

Spring-Shiro

导入依赖

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>

在容器内注入DefaultWebSecurityManager、ShiroFilter工厂ShiroFilterFactoryBean、密码匹配器HashedCredentialsMatcher、自己定义的Realm

 <!-- 
            dtd : document type definition    xxx.dtd  xxx2.dtd
            xsd : xml schema definition       xxx.xsd  xxx2.xsd
            文档约束:标签,顺序,层级,属性
         -->
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" 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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!--  shiro配置  -->
    <!--  Realm  -->
    <bean id="myRealm" class="com.XXX.realm.MyRealm">
        <property name="userService" ref="userServiceImpl"/>
        <property name="roleService" ref="roleServiceImpl"/>
        <property name="permissionService" ref="permissionServiceImpl"/>
        <!--  密码比对器  -->
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <property name="hashAlgorithmName" value="SHA-256"/>
                <!--  true means hex encoded, false means base64 encoded  -->
                <property name="storedCredentialsHexEncoded" value="false"/>
                <property name="hashIterations" value="10000"/>
            </bean>
        </property>
    </bean>
    <!--  声明SecurityManager  -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="myRealm"/>
        <!--  记住我管理器  -->
        <property name="rememberMeManager" ref="rememberMeManager"/>
        <!--  session管理器  -->
        <property name="sessionManager" ref="sessionManager"/>
    </bean>
    <!--  shiroFilter  -->
    <!--  生产SpringShiroFilter
             ( 持有shiro的过滤相关规则,可进行请求的过滤校验,校验请求是否合法 )
         -->
    <bean id="shiroFilter04" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!--  注入核心对象  -->
        <property name="securityManager" ref="securityManager"/>
        <!--  未登录,没权限时的跳转路径  -->
        <!-- <property name="loginUrl" value="/user/login"/>
                <property name="unauthorizedUrl" value="/user/perms/error"/> -->
        <!--  过滤器链  -->
        <!-- <property name="filterChainDefinitions">
                    <value>
                        /user/all=authc,roles["banzhang"]
                        /user/logout=logout
                        &lt;!&ndash;/user/insert=authc,roles["banfu"]
                        /user/update=authc,perms[""student:update""]
                        /order/insert=authc,roles["xuewei"]
                        &ndash;&gt;
                    </value>
                </property> -->
    </bean>
    <!--  记住我的Cookie  -->
    <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        <!--  rememberMe是cookie值中的key,value时用户名的密文
                     cookie["rememberMe":"deleteMe"] 此cookie每次登陆后都会写出,用于清除之前的cookie
                     cookie["rememberMe":username的密文] 此cookie也会在登录后写出,用于记录最新的username
                    (ops: 如上设计,既能保证每次登陆后重新记录cookie,也能保证切换账号时,记录最新账号)
                 -->
        <property name="name" value="rememberMe04"/>
        <!--  cookie只在http请求中可用,那么通过js脚本将无法读取到cookie信息,有效防止cookie被窃取  -->
        <property name="httpOnly" value="true"/>
        <!--  cookie的生命周期,单位:秒  -->
        <property name="maxAge" value="604800"/>
        <!--  7天  -->
    </bean>
    <!-- 记住我管理器 -->
    <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
        <property name="cookie" ref="rememberMeCookie"/>
    </bean>
    <!--  会话Cookie模板 默认可省 -->
    <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        <!--  cookie的 key="sid"  -->
        <property name="name" value="JSESSIONID04"/>
        <!--  只允许http请求访问cookie  -->
        <property name="httpOnly" value="true"/>
        <!--  cookie过期时间,-1:存活一个会话 ,单位:秒 ,默认为-1 -->
        <property name="maxAge" value="-1"/>
    </bean>
    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <!--  默认值和配置中给出的一致,所bean:sessionIdCookie 可以省略  -->
        <property name="sessionIdCookie" ref="sessionIdCookie"/>
        <!--  session全局超时时间, 单位:毫秒 ,30分钟 默认值为1800000 -->
        <property name="globalSessionTimeout" value="1800000"/>
        <!--  注册session监听器  -->
        <property name="sessionListeners">
            <list>
                <bean class="com.XXX.session.MySessionListener"/>
            </list>
        </property>
        <!--  session检测的时间间隔  -->
        <property name="sessionValidationInterval" value="15000"/>
    </bean>
</beans>

其他相关的配置

        <!-- 
            dtd : document type definition    xxx.dtd  xxx2.dtd
            xsd : xml schema definition       xxx.xsd  xxx2.xsd
            文档约束:标签,顺序,层级,属性
         -->
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" 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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!--  导入外部参数文件  -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--  连接池:druid  -->
    <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <!--  基本属性 url、user、password  -->
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
        <!--  配置初始化大小、最小、最大  -->
        <property name="initialSize" value="1"/>
        <property name="minIdle" value="1"/>
        <property name="maxActive" value="${jdbc.maxPoolSize}"/>
        <!--  配置获取连接等待超时的时间  -->
        <property name="maxWait" value="3000"/>
        <!--  配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒  -->
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
        <!--  配置一个连接在池中最小空闲的时间,单位是毫秒  -->
        <property name="minEvictableIdleTimeMillis" value="300000"/>
        <property name="validationQuery" value="SELECT 'x'"/>
        <property name="testWhileIdle" value="true"/>
        <property name="testOnBorrow" value="false"/>
        <property name="testOnReturn" value="false"/>
    </bean>
    <!--  SqlSessionFactory
             配置: 1.连接池
                  2.mapper文件信息
                  3.别名-可选
                  4.插件-可选
          -->
    <bean id="sqlSessionFactory04" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- <property name="configLocation" value="classpath:configuration.xml"/> -->
        <!--  连接池  -->
        <property name="dataSource" ref="druid"/>
        <!--  mapper文件
                     如果和dao接口在同包且同名,则如下注入可以省略
                  -->
        <!-- <property name="mapperLocations" value="classpath:com/XXX/dao/*.xml"/> -->
        <!--  别名  -->
        <property name="typeAliasesPackage" value="com.XXX.pojo"/>
    </bean>
    <!--  扫描所有mapper,为每个dao定制实现类   sqlSession.getMapper(UserDAO.class)
             DAO实现的对象会 纳入工厂,并且beanID=DAO的首字母小写接口类名=“userDAO”
             1> DAO接口
             2> DAO映射文件
             3> SqlSession
         -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--  如果当前工厂中 只有一个SqlSessionFactory,则此项注入可以省略  -->
        <!-- <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory04"/> -->
        <!--  DAO接口位置  -->
        <!-- <property name="basePackage" value="com.XXX.dao,com.XXX2.dao2"/> -->
        <property name="basePackage" value="com.XXX.dao"/>
    </bean>
    <!--  事务管理  -->
    <!--  事务管理器  -->
    <bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="druid"/>
    </bean>
    <!--  支持 @Transactional 实现事务  -->
    <tx:annotation-driven transaction-manager="tx"/>
    <!--  配置注解扫描:让spring去发现注解,进而实现对应的功能  -->
    <context:component-scan base-package="com.XXX">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    <import resource="classpath:shiro_applicationContext.xml"/>
</beans>

配置web.xml

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">
    <display-name>Archetype Created Web Application</display-name>
    <!--  前端控制器  -->
    <servlet>
        <servlet-name>mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:mvc.xml</param-value>
        </init-param>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <!--  spring启动配置  -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <!--
      DelegatingFilterProxy是filter的代理类,它会内部通过this.getFilterName()方法获取到自己的名字,并将自己的名字作为被代理对象的名字,
      然后从spring工厂中获取和它同名的bean,(id="shiroFilter"),接到请求后调用bean的doFilter方法,进行访问控制。
      如果不指定名称,则默认名称为delegatingFilterProxy
       -->
    <filter>
        <filter-name>shiroFilter04</filter-name>    <!--指定filter的名字-->
        <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>shiroFilter04</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


    <filter>
        <filter-name>encoding</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

DelegatingFilterProxy的作用与用法

Shiro整合SpringBoot

核心代码

package org.example.shiro.config;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.example.shiro.realm.MyRealm;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;

@Configuration
public class ShiroConfig {
    //密码认证器
    @Bean
    public HashedCredentialsMatcher getHashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("sha-256");
        hashedCredentialsMatcher.setHashIterations(10000);
        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(false);
        return hashedCredentialsMatcher;
    }
    //Realm
    @Bean
    public AuthorizingRealm getAuthorizingRealm(HashedCredentialsMatcher matcher){
        MyRealm myRealm = new MyRealm();
        myRealm.setCredentialsMatcher(matcher);
        return myRealm;
    }
    //SecurityManager
    @Bean
    public SecurityManager getSecurityManager(AuthorizingRealm realm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(realm);
        return defaultWebSecurityManager;
    }

    @Bean
    public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
        DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
        //设置不认证可以访问的资源

        definition.addPathDefinition("/myController/userLogin","anon");
        definition.addPathDefinition("/myController/Login","anon");
        definition.addPathDefinition("/login","anon");
        //设置需要进行登录认证的拦截范围
        definition.addPathDefinition("/**","authc");
        return definition;
    }
    //ShiroFilter
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean getShiroFilter(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("");
        shiroFilterFactoryBean.setUnauthorizedUrl("/user/login");
        shiroFilterFactoryBean.setFilterChainDefinitions("");
        return shiroFilterFactoryBean;
    }
    //Filter代理类,注册到过滤器链上
    @Bean
    public FilterRegistrationBean<DelegatingFilterProxy> getDelegatingFilterProxy(){
        FilterRegistrationBean<DelegatingFilterProxy> filterFilterRegistrationBean = new FilterRegistrationBean<>();
        DelegatingFilterProxy delegatingFilterProxy = new DelegatingFilterProxy();
        delegatingFilterProxy.setTargetBeanName("shiroFilter");
        delegatingFilterProxy.setTargetFilterLifecycle(true);
        filterFilterRegistrationBean.setFilter(delegatingFilterProxy);
        filterFilterRegistrationBean.addUrlPatterns("/*");
        return filterFilterRegistrationBean;
    }
}

package org.example.shiro.realm;

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.example.shiro.pojo.User;
import org.example.shiro.service.PermissionServiceImpl;
import org.example.shiro.service.RoleServiceImpl;
import org.example.shiro.service.UserServiceImpl;
import org.springframework.web.context.ContextLoader;

import java.util.Set;

// shiro在认证的过程中会先调用Realm获取用户信息、角色信息、权限信息,realm会在shiroFilter执行,
public class MyRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("Shiro正在获取角色权限信息");
        String userName = (String) principalCollection.getPrimaryPrincipal();
        RoleServiceImpl roleServiceImpl = ContextLoader.getCurrentWebApplicationContext().getBean("roleServiceImpl", RoleServiceImpl.class);
        PermissionServiceImpl permissionServiceImpl = ContextLoader.getCurrentWebApplicationContext().getBean("permissionServiceImpl", PermissionServiceImpl.class);
        // 获取角色信息
        Set<String> roleNames = roleServiceImpl.getRoleNameByUserName(userName);
        // 获取权限信息
        Set<String> permissionNames = permissionServiceImpl.getPermissionNameByUserName(userName);

        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(roleNames);
        simpleAuthorizationInfo.setStringPermissions(permissionNames);

        return simpleAuthorizationInfo;
    }

    /**
     * 作用:查询身份信息,返回即可,不用做任何比对,交给AuthenticationFilter进行比对
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("Shiro正在获取用户信息");
        String userName = (String) authenticationToken.getPrincipal();
        UserServiceImpl userServiceImpl = ContextLoader.getCurrentWebApplicationContext().getBean("userServiceImpl", UserServiceImpl.class);
        User currentUser = userServiceImpl.getUserByUserName(userName);
        // 到时候AuthenticationFilter会拿simpleAuthenticationInfo里面的password和用户输入的password做比对
        //SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(currentUser.getUserName(), currentUser.getPassword(), this.getName());
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(currentUser.getUserName(), currentUser.getPassword(), ByteSource.Util.bytes(currentUser.getSalt()), this.getName());
        return simpleAuthenticationInfo;
    }
}


可参考1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值