shiro学习笔记:spring整合shiro

使用spring整合shiro框架步骤:

1.导入jar包

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.3.2</version>
        </dependency>    
View Code

2.配置shiro配置文件

<?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:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">

    <!--shiro中的类型要交个spring容器管理-->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

    <!--安全管理器-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!--缓存管理器-->
        <property name="cacheManager" ref="cacheManager"/>
        <!--会话的模式-->
        <property name="sessionMode" value="native"/>
        <!--配置realm-->
        <property name="realm" ref="loginRealm"/>
    </bean>

    <!--缓存管理器-->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"/>

    <!--配置自己的Realm-->
    <bean id="loginRealm" class="com.zs.realm.LoginRealm">
        <property name="name" value="loginRealm"/>
        <!--凭证(密码)匹配器-->
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <!--算法-->
                <property name="hashAlgorithmName" value="MD5"/>
                <!--存储的凭据16进制编码-->
                <property name="storedCredentialsHexEncoded" value="true"/>
                <!--迭代次数-->
                <property name="hashIterations" value="1024"/>
            </bean>
        </property>
    </bean>

    <!--启用注解配置-->
    <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>

    <!--配置shiro过滤器,需要在web.xml中配置过滤器,且过滤器的名字要与这里的id一致-->
    <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="/view/index.jsp"/>
        <!--配置未授权地址-->
        <property name="unauthorizedUrl" value="/unauthorized.jsp"/>

        <!--配置默认过滤器-->
        <property name="filterChainDefinitions">
            <value>
                /login=anon
                /home=authc
                /logout=logout
                /static/** =anon
            </value>
        </property>
    </bean>
</beans>

关于默认过滤器:

3.配置web.xml过滤器

 注意过滤器名字要与shiro.xml中的过滤器名字相同。

 <!--shiro过滤器-->
    <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>

4.将shiro配置文件导入spring主配置文件:在applicationContext.xml中添加如下代码

至此,spring整合shiro的配置已经完成:下面编辑web页面进行测试:

编辑登陆页面:

编写Realm,要注意在上面的配置文件中,配置了Realm,这里编辑的Realm需要配置在上面的配置文件中;

package com.zs.realm;

import org.apache.shiro.authc.*;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.util.ByteSource;
import org.junit.jupiter.api.Test;

public class LoginRealm extends AuthenticatingRealm {
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        String username = usernamePasswordToken.getUsername();
//        模拟从数据库中获取到的密码,并进行hash算法加密
        ByteSource salt = ByteSource.Util.bytes(username);
//     参数:"MD5":加密算法,"123456":模拟从数据库中取到的密码,salt:盐:为了使数据更安全,后面详述,1024:迭代次数 SimpleHash simpleHash
= new SimpleHash("MD5","123456",salt,1024); String password = simpleHash.toHex(); SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password,salt,this.getName()); return info; } }

编辑控制器:

package com.zs.controller;

import com.zs.entity.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttributes;

@Controller
public class UserController {

    private static final transient Logger log = LoggerFactory.getLogger(UserController.class);
    @RequestMapping("/login")
    public String login(User user)  {

        Subject currentUser = SecurityUtils.getSubject();

        //判断是否认证
        if (!currentUser.isAuthenticated()) {
            UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());
            //记住我
//            token.setRememberMe(true);
            try {
                currentUser.login(token);
            } catch (UnknownAccountException uae) {
//                找不到用户
                log.info("There is no user with username of " + token.getPrincipal());
            } catch (IncorrectCredentialsException ice) {
//                密码错误
                log.info("Password for account " + token.getPrincipal() + " was incorrect!");
            } catch (LockedAccountException lae) {
//                账户锁定
                log.info("The account for username " + token.getPrincipal() + " is locked.  " +
                        "Please contact your administrator to unlock it.");
            } catch (AuthenticationException ae) {
                //unexpected condition?  error?
            }
        }
        return "redirect:/home";
    }

    @RequestMapping("/home")
    public String home() {
        System.out.println("hello");
        return "index";
    }
}

准备完毕,运行测试,123456为能登陆成功的密码,输入正确的密码和错误的密码进行测试。

在spring中shiro的工作流程

在上一篇我们已经写了shiro的主要工作流程架构,前端第三方用户subject发送请求,到安全管理器securityManager,安全管理器交给内部的子类组件进行处理,通过底层的realm获取数据域,进行验证授权等操作。

在spring整合的shiro中,前端发送请求,会先到web.xml中的过滤器,然后过滤器根据名字找到配置文件中的过滤器配置:

通过比对该过滤器中的配置信息,判断哪儿些请求需要认证,哪儿些请求需要权限验证等

将请求经过shiro的安全认证后,在决定哪儿些请求可以放行,哪儿些请求需要认证、权限、角色等。请求发送到控制器后,控制器执行到这里:

可以打断点,进入该方法,可以看到进入到了配置的LoginRealm数据域内,然后再该Realm内,进行密码的匹配认证,判断是否认证成功,具体可以一步一步的进入源码查看,底层是通过equals方法来进行判断的。

关于密码加密:

在之前我们的数据库用户表的密码数据都是没有经过加密的,这样的方式不安全,如果一个员工是管理数据库的,那么可以通过一句sql命令就查看到所有用户的账号密码,直接进行登录操作,如果涉及到隐私,财产安全等,。。。因此这种方式是很不安全的,使用shiro框架后,会将前端的密码进行加密,这里使用MD5方式加密,使用MD5加密的方式是不可逆的,也就是说只能加密,不能解密。这种加密方式会将你的密码生成一个32位的字符串,这样就可以提高数据的安全性:

这是我们在shiro配置文件中配置的密码加密,他会将密码按照MD5的算法进行加密,加密迭代的次数为1024次,

关于迭代:假如说原始密码是123,经过一次迭代后是aaa,然后将aaa作为密码进行第二次迭代得到密码bbb,再将bbb作为密码进行第三次迭代。。。。。。

由此可见,迭代的次数越多,那么密码就越安全,但是如果说数据库中有很多用户的密码是相同的,那么经过1024次迭代后,生成的字符串也是相同的,这样同样数据不安全,因为如果管理数据库的人看到这么多的重复的字符串,那么只需要破解出来一个字符串的原始密码,就可以知道其他用户的密码,因此我们就给他加盐salt

如上图所示,本来我们只需要将123456,按照MD5算法进行1024次迭代,生成密码的字符串,但是这种方式不安全,于是就将用户名作为盐salt,一块添加到密码的加密过程中,因为用户名是唯一的,每个人的用户名都不一样,因此生成的加密后的字符串也都不一样,这样就提高了数据的安全性。

使用加密后的工作流程

前端发送密码到shiro,然后shiro将密码进行加密,将加密后的字符串与数据库中存储的字符串进行比较,如果相同则登录成功,如果不同则登录不成功

关于Realm

Realm是shiro最底层的连接shiro与数据库数据的桥梁,shiro通过Realm连接数据库数据进行数据的比较,要使用realm需要是实现Realm接口:

package com.zs.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.realm.Realm;

public class MyRealm implements Realm {
    @Override
    public String getName() {
        return null;
    }

    @Override
    public boolean supports(AuthenticationToken token) {
        return false;
    }

    @Override
    public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        return null;
    }
}

可以看到,需要实现三个方法,为了使用方便,我们继承Realm的子类,子类封装好了一些方法,因此我们只需要重写我们需要的方法就好了,不需要每个方法都写。

Realm的实现类有很多,我们只继承我们需要的实现类,如:

package com.zs.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.realm.Realm;

public class MyRealm extends AuthenticatingRealm {
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        return null;
    }
}

这是个认证的Realm,我们只需要实现认证的方法,其他的方法 ,父类已经是实现过了

posted @ 2019-08-13 11:20  Zs夏至  阅读(222)  评论(0编辑  收藏  举报