在SSM中使用shiro实现登录验证(附密码加密)

  第一步:导入需要的jar:(maven方式)

    <properties>
        <!-- log4j日志文件管理包版本 -->
        <slf4j.version>1.7.7</slf4j.version>
        <log4j.version>1.2.17</log4j.version>
    </properties>

        <!-- log start -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>


        <!-- 格式化对象,方便输出日志 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.1.41</version>
        </dependency>


        <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>
        <!-- log end -->

        <!-- shiro -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-all</artifactId>
            <version>1.3.2</version>
        </dependency>

        <!-- ehcache -->
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache-core</artifactId>
            <version>2.6.11</version>
        </dependency>

  第二步:在web.xml中配置shiroFilter

   <!-- Shiro Filter is defined in the spring application context: -->
    <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>

  第三步:新建类UserRealm

package com.ang.elearning.shiro;

import javax.annotation.Resource;


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.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import com.ang.elearning.po.User;
import com.ang.elearning.service.IUserService;

public class UserRealm extends AuthorizingRealm {

    @Resource
    IUserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        User user = null;
        // 1. 把AuthenticationToken转换为UsernamePasswordToken
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        // 2. 从UsernamePasswordToken中获取email
        String email = upToken.getUsername();
        // 3. 若用户不存在,抛出UnknownAccountException异常
        user = userService.getUserByEmail(email);
        if (user == null)
            throw new UnknownAccountException("用户不存在!");
        // 4.
        // 根据用户的情况,来构建AuthenticationInfo对象并返回,通常使用的实现类为SimpleAuthenticationInfo
        // 以下信息从数据库中获取
        // (1)principal:认证的实体信息,可以是email,也可以是数据表对应的用户的实体类对象
        Object principal = email;
        // (2)credentials:密码
        Object credentials = user.getPassword();
        // (3)realmName:当前realm对象的name,调用父类的getName()方法即可
        String realmName = getName();
        // (4)盐值:取用户信息中唯一的字段来生成盐值,避免由于两个用户原始密码相同,加密后的密码也相同
        ByteSource credentialsSalt = ByteSource.Util.bytes(email);
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt,
                realmName);
        return info;
    }

}

  第四步:在src目录下新建ehcache.xml

<!--
  ~ Hibernate, Relational Persistence for Idiomatic Java
  ~
  ~ License: GNU Lesser General Public License (LGPL), version 2.1 or later.
  ~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
  -->
<ehcache>

    <!-- Sets the path to the directory where cache .data files are created.

         If the path is a Java System Property it is replaced by
         its value in the running VM.

         The following properties are translated:
         user.home - User's home directory
         user.dir - User's current working directory
         java.io.tmpdir - Default temp file path -->
    <diskStore path="./target/tmp"/>


    <!--Default Cache configuration. These will applied to caches programmatically created through
        the CacheManager.

        The following attributes are required for defaultCache:

        maxInMemory       - Sets the maximum number of objects that will be created in memory
        eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element
                            is never expired.
        timeToIdleSeconds - Sets the time to idle for an element beforeQuery it expires. Is only used
                            if the element is not eternal. Idle time is now - last accessed time
        timeToLiveSeconds - Sets the time to live for an element beforeQuery it expires. Is only used
                            if the element is not eternal. TTL is now - creation time
        overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache
                            has reached the maxInMemory limit.

        -->
    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
        />

    <!--Predefined caches.  Add your cache configuration settings here.
        If you do not have a configuration for your cache a WARNING will be issued when the
        CacheManager starts

        The following attributes are required for defaultCache:

        name              - Sets the name of the cache. This is used to identify the cache. It must be unique.
        maxInMemory       - Sets the maximum number of objects that will be created in memory
        eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element
                            is never expired.
        timeToIdleSeconds - Sets the time to idle for an element beforeQuery it expires. Is only used
                            if the element is not eternal. Idle time is now - last accessed time
        timeToLiveSeconds - Sets the time to live for an element beforeQuery it expires. Is only used
                            if the element is not eternal. TTL is now - creation time
        overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache
                            has reached the maxInMemory limit.

        -->

    <!-- Sample cache named sampleCache1
        This cache contains a maximum in memory of 10000 elements, and will expire
        an element if it is idle for more than 5 minutes and lives for more than
        10 minutes.

        If there are more than 10000 elements it will overflow to the
        disk cache, which in this configuration will go to wherever java.io.tmp is
        defined on your system. On a standard Linux system this will be /tmp"
        -->
    <cache name="sampleCache1"
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="300"
        timeToLiveSeconds="600"
        overflowToDisk="true"
        />

    <!-- Sample cache named sampleCache2
        This cache contains 1000 elements. Elements will always be held in memory.
        They are not expired. -->
    <cache name="sampleCache2"
        maxElementsInMemory="1000"
        eternal="true"
        timeToIdleSeconds="0"
        timeToLiveSeconds="0"
        overflowToDisk="false"
        /> -->

    <!-- Place configuration for your caches following -->

</ehcache>

  第五步:在spring配置文件中配置shiro

    <!-- shiro start -->
    <!-- 1. 配置SecurityManager -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="cacheManager" ref="cacheManager" />
        <property name="authenticator" ref="authenticator"></property>
        <!-- 可以配置多个Realm,其实会把realms属性赋值给ModularRealmAuthenticator的realms属性 -->
        <property name="realms">
            <list>
                <ref bean="userRealm" />
            </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>

    <!-- 3. 配置Realm -->
    <!-- 3.1 直接配置继承了org.apache.shiro.realm.AuthorizingRealm的bean -->
    <bean id="userRealm" class="com.ang.elearning.shiro.UserRealm">
        <!-- 配置密码匹配器 -->
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <!-- 加密算法为MD5 -->
                <property name="hashAlgorithmName" value="MD5"></property>
                <!-- 加密次数 -->
                <property name="hashIterations" value="1024"></property>
            </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>一致。 如果不一致,会抛出NoSuchBeanDefinitionException异常,因为shiro会在IOC容器中查找名称和<filter-name> 
        值一致的filter bean -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="/login.jsp" />
        <property name="successUrl" value="/WEB-INF/user/index.jsp" />
        <property name="unauthorizedUrl" value="/login.jsp" />
        <!-- 配置哪些页面需要受保护,以及访问这些页面需要的权限 -->
        <property name="filterChainDefinitions">
            <value>
                <!-- 第一次匹配优先的原则 -->
                /login.jsp = anon
                /user/login = anon

                /logout = logout

                /** = authc
            </value>
        </property>
    </bean>

    <!-- 7. 配置ModularRealmAuthenticator,可以实现多Realm认证 -->
    <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
        <!-- 配置认证策略,只要有一个Realm认证成功即可,并且返回所有认证成功信息 -->
        <property name="authenticationStrategy">
            <bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean>
        </property>
    </bean>
    <!-- shiro end -->

  第六步:测试(Userservice,UserDAO等略)
  
  UserController:

@Controller
@RequestMapping("/user")
public class UserController {

    @Resource
    private IUserService userService;

    @RequestMapping(value = "login", method = RequestMethod.POST)
    public String login(@RequestParam("email") String email, @RequestParam("password") String password) {
        Subject currentUser = SecurityUtils.getSubject();
        if (!currentUser.isAuthenticated()) {
            UsernamePasswordToken upToken = new UsernamePasswordToken(email, password);
            upToken.setRememberMe(false);
            try {
                currentUser.login(upToken);
                return "user/index";
            } catch (IncorrectCredentialsException ice) {
                System.out.println("邮箱/密码不匹配!");
            } catch (LockedAccountException lae) {
                System.out.println("账户已被冻结!");
            } catch (AuthenticationException ae) {
                System.out.println(ae.getMessage());
            }
        }
        return "redirect:/login.jsp";
    }
}

login.jsp:

<form action="${pageContext.request.contextPath }/user/login" method="POST">
        邮箱:<input type="text" name="email"> 
        <br><br>
        密码:<input type="password" name="password"> 
        <br><br>
        <input type="submit" value="登录">
    </form>

/WEB-INF/user/index.jsp

<h4>Login Successfully!</h4>
<br>
<a href="${pageContext.request.contextPath }/logout">Logout</a>

补充:
shiro中默认的过滤器:

image_1b7kkc5n5r01kj614pekgk1p8l9.png-302.2kB

整个认证流程:
(1). 在Controller中通过Security.getSubject()获取当前的Subject;
(2). 通过Subject的isAuthenticated()验证当前用户是否已经被认证;
(3). 如果没有被认证,开始认证。
(4). 将从前台传来的用户名(邮箱)和密码封装到一个UsernamePasswordToken对象upToken中;
(5). 调用当前Subject的login(upToken)方法,这会把upToken作为参数传递到自定义的Realm的doGetAuthenticationInfo(AuthenticationToken)方法中;
(6). 在doGetAuthenticationInfo(AuthenticationToken)方法中,首先将AuthenticationToken转换为UsernamePasswordToken对象upToken,然后调用Service层,根据upToken中的用户名到数据库中查询密码;
(7). 由shiro完成密码的比对。密码的比对是通过AuthenticatingRealm的credentialsMatcher属性来进行比对的。

要实现特定Realm处理特定身份验证的效果,请参见《shiro实现不同身份使用不同Realm进行验证》

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值