SSM整合Shiro小Demo

写在前面

自己在B站上看了一个shiro的视频,花了三四个小时速成了一下shiro,大概了解了一下shiro的流程,然后自己做了一个小Demo,记录一下。

正文

话不多说,直接上代码

  1. 首先是使用Maven搭建一下SSM的工程,做好准备工作
    (如果有小伙伴不懂怎么使用Maven搭建SSM项目的话,可以参考我之前的博文https://blog.csdn.net/weixin_44215175/article/details/108642595
  2. 接着就是先引入shiro的相关依赖包,Maven的话在pom.xml中加入以下依赖:
<!--shiro版本-->
<shiro.version>1.3.2</shiro.version>
<!-- Shiro -->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-core</artifactId>
      <version>${shiro.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-web</artifactId>
      <version>${shiro.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-aspectj</artifactId>
      <version>${shiro.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-cas</artifactId>
      <version>${shiro.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-ehcache</artifactId>
      <version>${shiro.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-hazelcast</artifactId>
      <version>${shiro.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-guice</artifactId>
      <version>${shiro.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-quartz</artifactId>
      <version>${shiro.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>${shiro.version}</version>
    </dependency>
  1. 同时需要slf4j和ehcache,也一并引入
<!-- slf4j -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>${slf4j.version}</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>${slf4j.version}</version>
    </dependency>

    <!--ehcache-->
    <dependency>
      <groupId>net.sf.ehcache</groupId>
      <artifactId>ehcache</artifactId>
      <version>2.8.3</version>
    </dependency>
  1. 接下来就是配置shiro
    在spring的配置文件中加入以下配置:
<!--
        1.配置securityManager!
     -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="cacheManager" ref="cacheManager"/>
<!--        <property name="realm" ref="jdbcRealm"/>-->
        <property name="authenticator" ref="authenticator" />
        <property name="realms">
            <list>
                <ref bean="jdbcRealm" />
            </list>
        </property>
    </bean>

    <!--
        2.配置cacheManager,缓存
        2.1 需要加入ehcache的jar以及配置文件
     -->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
    </bean>

    <bean id="authenticator"
            class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
        <property name="authenticationStrategy">
            <bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"/>
        </property>
    </bean>

    <!--
        3.配置realm
        3.1 直接配置了实现了Realm接口的bean
    -->
    <bean id="jdbcRealm" class="pers.kuroko.realm.ShiroRealm">
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <property name="hashAlgorithmName" value="MD5" />
                <property name="hashIterations" value="1024" />
                <property name="storedCredentialsHexEncoded" value="true" />
            </bean>
        </property>
    </bean>

    <!--
        4. 配置lifecycleBeanPostProcessor,可以自动地来调用配置在Spring Ioc容器中shiro bean的生命周期方法
    -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

    <!--
        5. 启用IOC容器中使用shiro的注解,但必须配置lifecycleBeanPostProcessor之后才可以使用
    -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
          depends-on="lifecycleBeanPostProcessor"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

    <!--
        6. 配置shiroFilter
        6.1 id必须和web.xml文件中配置的DelegatingFilterProxy的<filter-name>一致;
            若不一致,则会抛出异常,因为shiro回来IoC容器中查找和<filter-name>名字对应的filter bean
        6.2
    -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/view/login.jsp"/><!--登录页面-->
        <property name="successUrl" value="/index.jsp"/><!--登录成功页面-->
        <property name="unauthorizedUrl" value="/view/unauthorized.jsp"/><!--没有权限页面-->
<!--        <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap" />-->
        <!--
            配置哪些页面需要受保护
            以及访问这些页面需要的权限.
            1. anon 可以被匿名访问
            2. authc 必须认证(登录)后才访问
            3. logout 登出
            3. 匹配原则是第一次优先匹配
            4. roles 角色过滤器
        -->
        <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap" />
    </bean>

    <!-- 配置一个bean,该bean实际上是一个Map,通过实例工厂方法的方式 -->
    <bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapBuilder"
          factory-method="buildFilterChainDefinitionMap" />
    <bean id="filterChainDefinitionMapBuilder" class="pers.kuroko.factory.FilterChainDefinitionMapBuilder" />

这里我是采用配置一个filterChainDefinitionMap的方式来配置shiro的url拦截

  1. 创建FilterChainDefinitionMapBuilder
    在这里配置具体的拦截方式
import java.util.LinkedHashMap;

public class FilterChainDefinitionMapBuilder {
    
    

    public LinkedHashMap<String, String> buildFilterChainDefinitionMap() {
    
    
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        // 应该操作数据库
        map.put("/view/register.jsp", "anon");
        map.put("/view/login.jsp", "anon");

        map.put("/index/doLogin", "anon");
        map.put("/index/doRegister", "anon");
        map.put("/index/logout", "logout");
        map.put("/view/admin.jsp", "roles[admin]");
        map.put("/**", "authc");
        return map;
    }

}
  1. 编写自己的Realm
import org.apache.shiro.authc.*;
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 pers.kuroko.entity.User;
import pers.kuroko.service.UserService;

import java.util.HashSet;
import java.util.Set;

public class ShiroRealm extends AuthorizingRealm {
    
    

    @Autowired
    private UserService userService;

    // 授权会被调用的方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    
    
        // 1. 从PrincipalCollection中来获取登录用户的信息
        Object principal = principals.getPrimaryPrincipal();
        User user = userService.selectUserByUsername((String) principal);
        // 2. 利用登录的用户的信息来获取当前用户的角色或权限(查询数据库)
        Set<String> roles = new HashSet<>();
        roles.add(user.getRole());
        // 3. 创建SimpleAuthenticationInfo对象,并设置其reles属性
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
        // 4. 返回SimpleAuthenticationInfo对象
        return info;
    }

    // 登录会被调用的方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
    
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)token;
        if (usernamePasswordToken.getUsername() == null || "".equals(usernamePasswordToken.getUsername())) {
    
    
            return null;
        }
        String username = usernamePasswordToken.getUsername();
        User user = userService.selectUserByUsername(username);
        // 以下信息是从数据库中获取的
        // 1) principle:认证的实体信息,可以是username,也可以是数据表对应的用户的实体类对象
        Object principle = username;
        // 2)credentials:密码
        Object credentials = user.getUpwd();
        // 3)realmName:当前realm 对象的 name,调用父类的getName()方法即可
        String realmName = getName();
        // 4)盐值
        ByteSource credentialsSalt = ByteSource.Util.bytes(username);
        return new SimpleAuthenticationInfo(principle, credentials, credentialsSalt, realmName);
    }

}

  1. 编写Controller测试
@Controller("indexController")
@RequestMapping("/index")
public class IndexController {
    
    

    @Autowired
    private UserService userService;
    Logger logger = LoggerFactory.getLogger(IndexController.class);

    @RequestMapping("/doLogin")
    public String doLogin(@RequestParam("uname") String uname,
                          @RequestParam("upwd") String upwd, Model model) {
    
    
        Subject curUser = SecurityUtils.getSubject();
        if (!curUser.isAuthenticated()) {
    
    
            // 把用户名和密码封装成 UsernamePasswordToken对象
            UsernamePasswordToken token = new UsernamePasswordToken(uname, upwd);
            // rememberMe
            token.setRememberMe(true);
            try {
    
    
                // 执行登录
                // 执行了ShiroRealm中的doGetAuthenticationInfo方法
                curUser.login(token);
            }
            // 所有认证时异常的父类
            catch (AuthenticationException ae) {
    
    
                logger.info("登录失败,失败原因:[{}]", ae.getMessage());
                model.addAttribute("message", "用户名或密码错误");
                return "login";
            }
        }
        return "redirect:/index.jsp";
    }

    @RequestMapping("/doRegister")
    public String doRegister(@RequestParam("uname") String uname,
                             @RequestParam("upwd") String upwd, Model model) {
    
    
        if (userService.selectUserByUsername(uname) != null) {
    
    
            model.addAttribute("message", "用户已存在");
            return "register";
        }
        Object result = new SimpleHash("MD5", upwd, ByteSource.Util.bytes(uname), 1024);
        User user = new User(uname, result.toString(), "user");
        if (userService.addUser(user) > 0) {
    
    
            return "redirect:login";
        } else {
    
    
            model.addAttribute("message", "注册失败");
            return "register";
        }
    }

}
  1. 其他的Service和dao等都是简单的数据库操作
  2. 测试
    运行结果:
    在这里插入图片描述
    登录成功后:
    在这里插入图片描述
    用admin登录,可以访问Admin页面
    在这里插入图片描述
    当使用其他用户登录时,由于没有访问Admin页面的权限,所以会跳转到无权限页面
    在这里插入图片描述

写在后面

此Demo仅仅是简单演示了一下shiro实现的权限控制,真实使用到开发中还需要进行改进
本人仅仅是初学者,可能实现Demo不完善,觉得哪里不对的,还请大家多多指教

本项目完整Demo已上传GitHub,地址:https://github.com/Fjz-Kuroko/ShiroSSMDemo

猜你喜欢

转载自blog.csdn.net/weixin_44215175/article/details/111187778