java:shiro(认证,赋予角色,授权...)

1.shiro(权限框架(认证,赋予角色,授权...)):

 

  

  

 

 

 

  

 

   readme.txt(运行机制):

  

    1.从jsp的form中的action属性跳转到springmvc的Handler中(controller)
    2.首先获取Subject,因为securityManager在applicationContext.xml中配置过了,所以可以直接
      通过Subject currentUser = SecurityUtils.getSubject();
    3.开始认证
      首先把账号和密码封装进UsernamePasswordToken中,然后调用subject的login(UsernamePasswordToken);
    4.真正的认证开始(需要和数据库进行交互)

      select * from table where username="zhangsan"
      select * from table where username="zhangsan" and passowrd="123456"


      4.1 通过username先查询该登录用户是否存在,如果不存在就抛出异常
      4.2 如果登录用户存在,再次检验是否可以正常使用,如果不能正常使用就抛出异常
      4.3 匹配密码

    5.密码匹配流程
      5.1.从前端获取用户输入的密码
      5.2.从数据库中通过用户名查询密码
      5.3.把查询出的密码SimpleAuthenticationInfo
      5.4.shiro会通过SimpleCredentialsMatcher.class中doCredentialsMatch()进行匹配从前端传递的密码和从数据库中查询的密码是否一致
      5.4.1.doCredentialsMatch()会重写equals()来进行匹配if("123456".equals(user.getPassword()))
      5.4.2.该方法中需要两个参数
        AuthenticationToken(I)-实现类->UsernamePasswordToken(封装了前端传递的用户名和密码)
        用户名和密码
        AuthenticationInfo(I)-被继承->SaltedAuthenticationInfo(I)-实现类->SimpleAuthenticationInfo(username,     user.getPassword(), getName())
      username:获取密码使用
      user.getPassword():需要和前端进行匹配
      getName()-->realmName(ShiroRealm2)
      为授权做准备


    6.密码加密
      6.1.因为密码为明文,存入数据库中和项目运行过程中会在开发者工具中出现,所以存在安全隐患问题(如果用户密码遭遇泄漏,并且牵扯到金钱问题,发开公司将会承担用户的一切损失)


      6.2.进行为密码的加密


      shiro会在配置文件中指定加密方式(.ini配置文件,和spring整合以后会在applicationContext.xml中id="jdbcRealm")
      最终密码匹配是在SimpleCredentialsMatcher.class进行-->实现CredentialsMatcher(I)
      SimpleCredentialsMatcher.class下有一个子类,一旦使用指定shiro用MD5对密码进行加密的配置以后,密码匹配会在    Md5CredentialsMatcher.class中进行
      Md5CredentialsMatcher.class已经将来被废弃,所以
      since 1.1 - use the HashedCredentialsMatcher directly and set its
      最终对密码进行加密和匹配HashedCredentialsMatcher进行实现
      如何对明文密码进行加密


      6.2.1.从前端获取到用户输入的密码-->通过doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info)进行密码比对
      当进入方法的第一个参数时:AuthenticationToken-->首先会到org.apache.shiro.authc.credential.HashedCredentialsMatcher进行加密-->返回被加密过的密码
      -->返回给AuthenticationToken token
      -->进行对比AuthenticationToken token和AuthenticationInfo info(从数据库查询出的密码进行对比)
      shiro使用哪一种方式对密码进行明文加密(MD5)

      6.2.2.<!-- 指定密码的加密算法为MD5 -->
      <property name="credentialsMatcher">
      <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
      <!-- hashAlgorithmName:指定了shiro的加密方式,默认就是MD5,如果需要使用其他的加密方式,就必须自定义实现 -->
      <property name="hashAlgorithmName" value="MD5"></property>
      <!--指定使用MD5加密的次数-->
      <property name="hashIterations" value="1024"></property>
      </bean>
      </property>
      开启shiro的加密方式


      6.2.3.因为如果两个用户的密码相同,MD5加密后存入数据库也相同,需要使用"颜值"加密
      user.getPassword():从数据库中查询出的密码
      ByteSource.Util.bytes("1"):颜值
      ByteSource类型(I)
      里面会有一个内部实现类,可以构造出ByteSource实例对象
      This is slightly nicer than needing to know the implementation class to use.
      如果获取到颜值?
      ByteSource.Utils.bytes("字符串类型");返回值就是颜值所需要的类型(ByteSource)
      使用new SimpleAuthenticationInfo(username, user.getPassword(), ByteSource.Util.bytes("1"), getName());

    7.授权
      7.1.因为AuthenticatingRealm只做认证功能,那么授权在同一个class中无法处理
      7.2.授权需要使用到的class是AuthenticatingRealm一个子类AuthorizingRealm
      AuthorizingRealm.class既可以进行认证功能,也可以进行授权的功能

 

 

   LoginHandler.java:

package com.zzsxt.lee.web.shiro.controller;

import javax.servlet.http.HttpSession;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.zzsxt.lee.web.shiro.service.TestService;

@Controller
public class LoginHandler {

    @Autowired
    private TestService testService;

    @RequestMapping("/login.action")
    public String login(@RequestParam("username") String username, @RequestParam("password") String password) {

        // 开始进行登录
        System.out.println("我是处理登录请求的方法,我被访问过");

        Subject currentUser = SecurityUtils.getSubject();
        // let's login the current user so we can check against roles and
        // AuthenticatingRealm permissions:
        // currentUser.isAuthenticated()当前用户是否被认证
        // 如果当前用户被认证了,说明用户还是处于登录状态
        if (!currentUser.isAuthenticated()) {
            // 如果没有被认证,开始登录
            // 把需要登录的用户名和密码存入shiro提供的令牌中
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            try {
                // subject调用login方法来进行匹配用户是否可以登录成功
                // login方法的参数需要接收shiro的UsernamePasswordToken类型
                currentUser.login(token);
            } catch (AuthenticationException ae) {
                System.out.println("登录失败!" + ae.getMessage());
            }
        }
        return "redirect:success.jsp";
    }

    @RequestMapping("/annotation.action")
    public String ViewAdminPage(HttpSession session) {
        session.setAttribute("user", "lisi");
        // 如果没有权限调用test()方法的时候,就直接抛出异常
        try {
            testService.test();
        } catch (Exception e) {
            e.getMessage();
            return "redirect:unauthorized.jsp";
        }
        return "redirect:success.jsp";
    }

}

 

  User.java:

package com.zzsxt.lee.web.shiro.model;

import java.io.Serializable;

public class User implements Serializable {
    private static final long serialVersionUID = 3978937578087629248L;

    private Long id;
    private String username;
    private String password;
    private Integer age;
    private Integer lockedUser;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getLockedUser() {
        return lockedUser;
    }

    public void setLockedUser(Integer lockedUser) {
        this.lockedUser = lockedUser;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((age == null) ? 0 : age.hashCode());
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        result = prime * result + ((lockedUser == null) ? 0 : lockedUser.hashCode());
        result = prime * result + ((password == null) ? 0 : password.hashCode());
        result = prime * result + ((username == null) ? 0 : username.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        User other = (User) obj;
        if (age == null) {
            if (other.age != null)
                return false;
        } else if (!age.equals(other.age))
            return false;
        if (id == null) {
            if (other.id != null)
                return false;
        } else if (!id.equals(other.id))
            return false;
        if (lockedUser == null) {
            if (other.lockedUser != null)
                return false;
        } else if (!lockedUser.equals(other.lockedUser))
            return false;
        if (password == null) {
            if (other.password != null)
                return false;
        } else if (!password.equals(other.password))
            return false;
        if (username == null) {
            if (other.username != null)
                return false;
        } else if (!username.equals(other.username))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", username=" + username + ", password=" + password + ", age=" + age + ", lockedUser="
                + lockedUser + "]";
    }

}

 

  ShiroRealm.java:

package com.zzsxt.lee.web.shiro.reamls;

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

/**
 * @description 实现Realm接口
 * @author Seven Lee
 * @date 2017年9月21日 下午2:11:56
 *
 */
public class ShiroRealm implements Realm {

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

    @Override
    public String getName() {
        return null;
    }

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

 

  ShiroRealm2.java:

package com.zzsxt.lee.web.shiro.reamls;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.util.ByteSource;

import com.zzsxt.lee.web.shiro.model.User;

public class ShiroRealm2 extends AuthenticatingRealm {

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // token从controller中调用login(UsernamePasswordToken)传递过来
        // 1.把AuthenticationToken类型的token转换为UsernamePasswordToken
        // upToken:封装了从前端传递的用户名和密码(用户输入)
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        // 2.通过UsernamePasswordToken获取username
        String username = upToken.getUsername();
        // 3.通过username查询数据库
        System.out.println("从数据库中匹配username:" + username);

        // 4.如果没有查询到用户对象信息,就抛出找不到用户异常UnknownAccountException
        // 模拟通过username已经从数据库中查询出了User对象,而且用户存在
        User user = new User();
        user.setId(1L);
        user.setUsername("zhangsan");
        user.setPassword("123456");
        user.setAge(17);
        user.setLockedUser(0);

        if (!user.getUsername().equals(username)) {
            throw new UnknownAccountException("该用户不存在");
        }
        // 5.如果用户存在-->根据查询用户信息的情况,抛出其他的异常(该用户已经被锁定,用户锁定异常LockedAccountException)
        if (user.getLockedUser() == 1) {
            throw new LockedAccountException("该用户被锁定");
        }
        // 6.返回AuthenticationInfo类型的数据
        // principal:查询出的用户实体类对象,也可以是用户名
        // credentials:用户的密码
        // realmName:realm的name
        // SimpleAuthenticationInfo sact = new SimpleAuthenticationInfo(username, user.getPassword(), getName());
        SimpleAuthenticationInfo sact = new SimpleAuthenticationInfo(username, user.getPassword(), ByteSource.Util.bytes("1"), getName());
        return sact;
    }

    public static void main(String[] args) {
        // e10adc3949ba59abbe56e057f20f883e加密一次后的123456的密码
        // fc1709d0a95a6be30bc5926fdb7f22f4加密1024次后的123456的密码
        // ee74a75f182c46effa1a4b350d537566加完盐值(ByteSource.Util.bytes("1"))后的密码
        User user = new User();
        user.setId(1L);
        new SimpleHash("MD5", "123456", ByteSource.Util.bytes("1"), 1024);
    }
}

 

  ShiroRealm3.java:

package com.zzsxt.lee.web.shiro.reamls;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
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.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import com.zzsxt.lee.web.shiro.model.User;

public class ShiroRealm3 extends AuthorizingRealm {
    
    /**
     * @description 认证(登录功能)
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // token从controller中调用login(UsernamePasswordToken)传递过来
        // 1.把AuthenticationToken类型的token转换为UsernamePasswordToken
        // upToken:封装了从前端传递的用户名和密码(用户输入)
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        // 2.通过UsernamePasswordToken获取username
        String username = upToken.getUsername();
        // 3.通过username查询数据库
        System.out.println("从数据库中匹配username:" + username);

        // 4.如果没有查询到用户对象信息,就抛出找不到用户异常UnknownAccountException
        // 模拟通过username已经从数据库中查询出了User对象,而且用户存在
        User user1 = new User();
        user1.setId(1L);
        user1.setUsername("zhangsan");
        user1.setPassword("ee74a75f182c46effa1a4b350d537566");
        user1.setAge(17);
        user1.setLockedUser(0);
        
        User user2 = new User();
        user2.setId(1L);
        user2.setUsername("lisi");
        user2.setPassword("ee74a75f182c46effa1a4b350d537566");
        user2.setAge(20);
        user2.setLockedUser(0);

        if (!user2.getUsername().equals(username)) {
            throw new UnknownAccountException("该用户不存在");
        }
        // 5.如果用户存在-->根据查询用户信息的情况,抛出其他的异常(该用户已经被锁定,用户锁定异常LockedAccountException)
        if (user2.getLockedUser() == 1) {
            throw new LockedAccountException("该用户被锁定");
        }
        // 6.返回AuthenticationInfo类型的数据
        // principal:查询出的用户实体类对象,也可以是用户名
        // credentials:用户的密码
        // realmName:realm的name
        // SimpleAuthenticationInfo sact = new
        // SimpleAuthenticationInfo(username, user.getPassword(), getName());
        // 在认证中的第一个参数:
            // 可以是Username也可以是User实体类对象
            // 如果传的参数为username,那么在授权阶段,使用principals.getPrimaryPrincipal();获取到的就是Username
        SimpleAuthenticationInfo sact = new SimpleAuthenticationInfo(user2.getUsername(), user2.getPassword(),
                ByteSource.Util.bytes("1"), getName());
        return sact;
    }

    /**
     * @description 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 1.通过principals获取已经认证完毕的用户名
        //String username = (String) principals.getPrimaryPrincipal();
        // 2.根据用户名去数据库中查询该认证用户下角色/权限信息
//        Set<String> roles = new HashSet<String>();
//        roles.add("user");
//        if ("zhangsan".equals(username)) {
//            roles.add("admin");
//        }
        List<String> permissionList = new ArrayList<String>();
        permissionList.add("menu:book");
        // 3.把角色/权限信息封装进SimpleAuthorizationInfo.class
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //info.addRoles(roles);
        info.addStringPermissions(permissionList);
        // 4.返回SimpleAuthorizationInfo.class
        return info;
    }

}

 

  TestService.java:

package com.zzsxt.lee.web.shiro.service;

import java.util.Date;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresRoles;

/**
 * @description @Service是spring的注解,和shiro并没有关系
 *     @RequiresRoles是shiro的注解,如果要使用shiro的注解,就一定要在shiro的配置文件中进行配置
 *     再由shiro一同交给spring进行托管
 * 如果想使用shiro权限注解,必须要在shiro配置文件中进行配置,配置这个TestService.class
 *     <bena id="xxx" class="xx.xxx.xx.xx.Xxx.class"></bean>
 * @author Seven Lee
 * @date 2017年9月22日 下午4:14:52
 *
 */
public class TestService {

    @RequiresRoles({ "admin" })
    public void test() {
        System.out.println("------------------------" + new Date().toString() + "------------------------");
        String username = (String) SecurityUtils.getSubject().getSession().getAttribute("user");
        System.out.println(username);
    }
}

 

  applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context" xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- ========================================================= Shiro Core 
        Components - Not Spring Specific ========================================================= -->
    <!-- Shiro's main business-tier object for web-enabled applications (use 
        DefaultSecurityManager instead when there is no web environment) -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="cacheManager" ref="cacheManager" />
        <property name="sessionMode" value="native" />
        <property name="realm" ref="jdbcRealm" />
    </bean>
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:ehcache.xml" />
    </bean>
    <bean id="jdbcRealm" class="com.zzsxt.lee.web.shiro.reamls.ShiroRealm3">
        <!-- 指定密码的加密算法为MD5 -->
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <!-- hashAlgorithmName:指定了shiro的加密方式,默认就是MD5,如果需要使用其他的加密方式,就必须自定义实现 -->
                <property name="hashAlgorithmName" value="MD5"></property>
                <!--指定使用MD5加密的次数 -->
                <property name="hashIterations" value="1024"></property>
            </bean>
        </property>
    </bean>

    <!-- ========================================================= Shiro Spring-specific 
        integration ========================================================= -->
    <!-- 最终shiro的生命周期由spring的ioc容器进行托管 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

    <!-- Enable Shiro Annotations for Spring-configured beans. Only run after 
        the lifecycleBeanProcessor has run: -->
    <!-- 默认自动代理生成器(生成shiro对象) 开启shiro注解 必须要配置在lifecycleBeanPostProcessor之后 -->
    <bean
        class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
        depends-on="lifecycleBeanPostProcessor" />


    <!-- 配置 securityManager 可以用注解的形式实现 -->
    <bean
        class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <!-- securityManager:subject -->
        <property name="securityManager" ref="securityManager" />
    </bean>


    <!-- 这就是和你们至关重要的配置文件了!!!!! 和以后的java代码有关系 shiroFilter securityManager loginUrl:登录的路径 
        successUrl:登录成功跳转的路径 unauthorizedUrl:如果该用户没有权限,需要跳转的页面 -->
    <!-- <bean id="shiroFilter">和web.xml的<filter-name>shiroFilter</filter-name>保持一致 
        如果配置的不一致 报错org.springframework.beans.factory.NoSuchBeanDefinitionException: 
        No bean named 'shiroFilter' is defined -->
    <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="/success.jsp" />
        <!-- 在授权阶段才能进入该页面 如果已经认证没有权限的时候,才会触发 -->
        <property name="unauthorizedUrl" value="/unauthorized.jsp" />
        <!-- anon:如果用户匿名访问也可以访问成功 shiro直接放行,并不需要认证和授权 /login.jsp:路径 /login.jsp 
            = anon:login页面不需要认证和授权,可以直接访问 authc:如果访问该路径,就必须要认证和授权 /**:所有的路径 /login.jsp 
            = anon /** = authc 只有login.jsp不需要拦截,其他一切路径都需要进行认证和授权 /路径 = roles[admin] -->
        <property name="filterChainDefinitions">
            <value>
                <!-- shiro拦截配置优先选举规则 如果把/**配置到前面 后面所有配置都失效 如果配置的不是/** shiro会默认覆盖前面的配置 -->
                /login.* = anon
                /logout = logout
                <!-- roles[admin,user]
                    猜想:
                    如果roles[]中配置了两个角色,那么被认证的用户就必须拥有这两个角色才能正常访问该角色下路径
                    否则,就会被跳转到无权限页面
                    因为zhangsan用户即拥有user角色又拥有admin角色,所以能够访问该路径
                    如果把zhangsan用户下的user角色给删除调,则不能访问该路径
                    猜想正确
                 -->
                <!-- /success.jsp= roles[user] -->
                /** = authc
            </value>
        </property>
    </bean>
    
    <bean id="testService" class="com.zzsxt.lee.web.shiro.service.TestService"></bean>
</beans>

 

  ehcache.xml:

<!--
  ~ Licensed to the Apache Software Foundation (ASF) under one
  ~ or more contributor license agreements.  See the NOTICE file
  ~ distributed with this work for additional information
  ~ regarding copyright ownership.  The ASF licenses this file
  ~ to you under the Apache License, Version 2.0 (the
  ~ "License"); you may not use this file except in compliance
  ~ with the License.  You may obtain a copy of the License at
  ~
  ~     http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing,
  ~ software distributed under the License is distributed on an
  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  ~ KIND, either express or implied.  See the License for the
  ~ specific language governing permissions and limitations
  ~ under the License.
  -->

<!-- EhCache XML configuration file used for Shiro spring sample application -->
<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="java.io.tmpdir/shiro-spring-sample"/>


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

    The following attributes are required:

    maxElementsInMemory            - 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.
    overflowToDisk                 - Sets whether elements can overflow to disk when the in-memory cache
                                     has reached the maxInMemory limit.

    The following attributes are optional:
    timeToIdleSeconds              - Sets the time to idle for an element before it expires.
                                     i.e. The maximum amount of time between accesses before an element expires
                                     Is only used if the element is not eternal.
                                     Optional attribute. A value of 0 means that an Element can idle for infinity.
                                     The default value is 0.
    timeToLiveSeconds              - Sets the time to live for an element before it expires.
                                     i.e. The maximum time between creation time and when an element expires.
                                     Is only used if the element is not eternal.
                                     Optional attribute. A value of 0 means that and Element can live for infinity.
                                     The default value is 0.
    diskPersistent                 - Whether the disk store persists between restarts of the Virtual Machine.
                                     The default value is false.
    diskExpiryThreadIntervalSeconds- The number of seconds between runs of the disk expiry thread. The default value
                                     is 120 seconds.
    memoryStoreEvictionPolicy      - Policy would be enforced upon reaching the maxElementsInMemory limit. Default
                                     policy is Least Recently Used (specified as LRU). Other policies available -
                                     First In First Out (specified as FIFO) and Less Frequently Used
                                     (specified as LFU)
    -->

    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            />

    <!-- We want eternal="true" (with no timeToIdle or timeToLive settings) because Shiro manages session
expirations explicitly.  If we set it to false and then set corresponding timeToIdle and timeToLive properties,
ehcache would evict sessions without Shiro's knowledge, which would cause many problems
(e.g. "My Shiro session timeout is 30 minutes - why isn't a session available after 2 minutes?"
Answer - ehcache expired it due to the timeToIdle property set to 120 seconds.)

diskPersistent=true since we want an enterprise session management feature - ability to use sessions after
even after a JVM restart.  -->
    <cache name="shiro-activeSessionCache"
           maxElementsInMemory="10000"
           eternal="true"
           overflowToDisk="true"
           diskPersistent="true"
           diskExpiryThreadIntervalSeconds="600"/>

    <cache name="org.apache.shiro.realm.SimpleAccountRealm.authorization"
           maxElementsInMemory="100"
           eternal="false"
           timeToLiveSeconds="600"
           overflowToDisk="false"/>

</ehcache>

 

  log4j.properties:

#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#
log4j.rootLogger=INFO, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n

# General Apache libraries
log4j.logger.org.apache=WARN

# Spring
log4j.logger.org.springframework=WARN

# Default Shiro logging
log4j.logger.org.apache.shiro=TRACE

# Disable verbose logging
log4j.logger.org.apache.shiro.util.ThreadContext=WARN
log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN

 

  shiro-servlet.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" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
      http://www.springframework.org/schema/mvc
      http://www.springframework.org/schema/mvc/spring-mvc.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd 
      http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">


    <context:component-scan base-package="com.zzsxt.lee.web.shiro"></context:component-scan>

    <mvc:default-servlet-handler />
    <mvc:annotation-driven></mvc:annotation-driven>

    <!-- Jsp视图解析器 -->
    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

 

  web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    id="WebApp_ID" version="3.1">

    <!--spring入口文件的配置 -->
    <!-- 确定配置文件位置 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <!-- shiro入口程序 -->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <!-- 
            init(Config config){
                String targetBeanName = shiroFilter;
            }
         -->
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
        <!-- 第二种配置<bean id="">
                在web.xml中的filter标签里配置<init-param></init-param>
                <param-name>targetBeanName</param-name>一定写规范
                <param-value>abc</param-value>指向了<bean>的id
         -->
        <!-- <init-param>
            <param-name>targetBeanName</param-name>
            <param-value>abc</param-value>
        </init-param> -->
    </filter>

    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 配置spring 监听器,加载xml配置文件 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- DispatcherServlet:前端控制器 配置前端控制器servlet -->
    <servlet>
        <servlet-name>shiro</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 表示随WEB服务器启动 -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>shiro</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>


    <display-name>20170921_shiro_spring</display-name>
</web-app>

 

  admin.jsp:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
            + path + "/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">

<title>My JSP 'login.jsp' starting page</title>

</head>

<body>
    <h1>Admin Page</h1>
</body>
</html>

 

  login.jsp:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
            + path + "/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">

<title>My JSP 'login.jsp' starting page</title>

</head>

<body>
    <h1>Login Page</h1>
    <form action="/zzsxt/login.action" method="post">
        Username:<input type="text" name="username" /><br /> Password:<input
            type="password" name="password" /><br /> <input type="submit"
            value="submit" />
    </form>
</body>
</html>

 

  success.jsp:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro"%>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
            + path + "/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">

<title>My JSP 'login.jsp' starting page</title>

</head>

<body>
    <h1>Success Page</h1>
        <!-- shiro:principal
                从shiro中取出认证后传递到授权中的对象-可以为实体对象,也可以是username
                property="password":从认证中传递来的实体对象的属性(User:username,passowrd,age,lockedUser...)
                如果从认证传递过来的是一个username的话,则无需property="password"属性
         -->
        <h4>Welcome:<shiro:principal></shiro:principal></h4>
        <!-- shiro:guest
                无需认证直接可以访问    
         -->
            <shiro:guest>
                <a href="">登录</a>
            </shiro:guest>
        <shiro:hasRole name="admin">
            <shiro:hasPermission name="menu:books">
                <a href="/zzsxt/admin.action">admin page</a>
            </shiro:hasPermission>
            <shiro:hasPermission name="menu:book">
                <a href="/zzsxt/admin.action">user page</a>
            </shiro:hasPermission>
        </shiro:hasRole>
        <a href="/zzsxt/user.jsp">user page</a>
        <a href="/zzsxt/annotation.action">测试注解</a>
        <a href="/zzsxt/logout">登出</a>
</body>
</html>

 

  unauthorized.jsp:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
            + path + "/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">

<title>My JSP 'login.jsp' starting page</title>

</head>

<body>
    <h1>Unauthorized Page</h1>
</body>
</html>

 

  user.jsp:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
            + path + "/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">

<title>My JSP 'login.jsp' starting page</title>

</head>

<body>
    <h1>User Page</h1>
</body>
</html>

 

posted @ 2017-09-23 21:47  咫尺天涯是路人丶  阅读(7056)  评论(0编辑  收藏  举报