shiro框架学习以及整合spring

一、Shiro框架简单介绍

Apache Shiro是Java的一个安全框架,旨在简化身份验证和授权。Shiro在JavaSE和JavaEE项目中都可以使用。它主要用来处理身份认证,授权,企业会话管理和加密等。Shiro的具体功能点如下:

(1)身份认证/登录,验证用户是不是拥有相应的身份; 
(2)授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限; 
(3)会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的; 
(4)加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储; 
(5)Web支持,可以非常容易的集成到Web环境; 
Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率; 
(6)shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去; 
(7)提供测试支持; 
(8)允许一个用户假装为另一个用户(如果他们允许)的身份进行访问; 
(9)记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

Shiro的主要框架图:

对一些其中的方法的简单说明:

Subject

Subject即主体,外部应用与subject进行交互,subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。 Subject在shiro中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权

Authenticator

Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。

Authorizer

Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。

realm

Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。

注意:不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。

sessionManager

sessionManager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。

SessionDAO

SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。

CacheManager

CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。

Cryptography

Cryptography即密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。

二、shiro认证与授权的在Web中实现

第一步:添加jar包

第二步:配置web.xml

    <!-- shiro的filter -->
    <!-- shiro过虑器,DelegatingFilterProxy通过代理模式将spring容器中的bean和filter关联起来 -->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <!-- 设置true由servlet容器控制filter的生命周期 -->
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
        <!-- 设置spring容器filter的bean id,如果不设置则找与filter-name一致的bean-->
        <init-param>
            <param-name>targetBeanName</param-name>
            <param-value>shiroFilter</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

第三步:配置spring-shiro.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.2.xsd 
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">

<!-- web.xml中shiro的filter对应的bean -->
<!-- Shiro 的Web过滤器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
        <property name="loginUrl" value="/login.action" />
        <!-- 认证成功统一跳转到first.action,建议不配置,shiro认证成功自动到上一个请求路径 -->
        <property name="successUrl" value="/first.action"/>
        <!-- 通过unauthorizedUrl指定没有权限操作时跳转页面-->
        <property name="unauthorizedUrl" value="/refuse.jsp" />
        <!-- 自定义filter配置 -->
        <property name="filters">
            <map>
                <!-- 将自定义 的FormAuthenticationFilter注入shiroFilter中-->
                <entry key="authc" value-ref="formAuthenticationFilter" />
            </map>
        </property>
        
        <!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
        <property name="filterChainDefinitions">
            <value>
                <!-- 对静态资源设置匿名访问 -->
                /images/** = anon
                /js/** = anon
                /styles/** = anon
                <!-- 验证码,可匿名访问 -->
                /validatecode.jsp = anon
                
                <!-- 请求 logout.action地址,shiro去清除session-->
                /logout.action = logout
                <!--商品查询需要商品查询权限 ,取消url拦截配置,使用注解授权方式 -->
                <!-- /items/queryItems.action = perms[item:query]
                /items/editItems.action = perms[item:edit] -->
                <!-- 配置记住我或认证通过可以访问的地址 -->
                /index.jsp  = user
                /first.action = user
                /welcome.jsp = user
                <!-- /** = authc 所有url都必须认证通过才可以访问-->
                /** = authc
                <!-- /** = anon所有url都可以匿名访问 -->
                
            </value>
        </property>
    </bean>

<!-- securityManager安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="customRealm" />
        <!-- 注入缓存管理器 -->
        <property name="cacheManager" ref="cacheManager"/>
        <!-- 注入session管理器 -->
        <property name="sessionManager" ref="sessionManager" />
        <!-- 记住我 -->
        <property name="rememberMeManager" ref="rememberMeManager"/>
        
    </bean>

<!-- realm -->
<bean id="customRealm" class="cn.itcast.ssm.shiro.CustomRealm">
    <!-- 将凭证匹配器设置到realm中,realm按照凭证匹配器的要求进行散列 -->
    <property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>

<!-- 凭证匹配器 -->
<bean id="credentialsMatcher"
    class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
    <property name="hashAlgorithmName" value="md5" />
    <property name="hashIterations" value="1" />
</bean>

<!-- 缓存管理器 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
    </bean>

<!-- 会话管理器 -->
    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <!-- session的失效时长,单位毫秒 -->
        <property name="globalSessionTimeout" value="600000"/>
        <!-- 删除失效的session -->
        <property name="deleteInvalidSessions" value="true"/>
    </bean>

<!-- 自定义form认证过虑器 -->
<!-- 基于Form表单的身份验证过滤器,不配置将也会注册此过虑器,表单中的用户账号、密码及loginurl将采用默认值,建议配置 -->
    <bean id="formAuthenticationFilter" 
    class="cn.itcast.ssm.shiro.CustomFormAuthenticationFilter ">
        <!-- 表单中账号的input名称 -->
        <property name="usernameParam" value="username" />
        <!-- 表单中密码的input名称 -->
        <property name="passwordParam" value="password" />
        <!-- 记住我input的名称 -->
        <property name="rememberMeParam" value="rememberMe"/>
 </bean>

<!-- rememberMeManager管理器,写cookie,取出cookie生成用户信息 -->
    <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
        <property name="cookie" ref="rememberMeCookie" />
    </bean>
    <!-- 记住我cookie -->
    <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        <!-- rememberMe是cookie的名字 -->
        <constructor-arg value="rememberMe" />
        <!-- 记住我cookie生效时间30天 -->
        <property name="maxAge" value="2592000" />
    </bean>


</beans>

securityManager: 这个属性是必须的。

loginUrl: 没有登录的用户请求需要登录的页面时自动跳转到登录页面,不是必须的属性,不输入地址的话会自动寻找项目web项目的根目录下的”/login.jsp”页面。

successUrl: 登录成功默认跳转页面,不配置则跳转至”/”。如果登陆前点击的一个需要登录的页面,则在登录自动跳转到那个需要登录的页面。不跳转到此。

unauthorizedUrl: 没有权限默认跳转的页面。

Shiro中默认的过滤器:

 

 

过滤器名称 过滤器类 描述
anon org.apache.shiro.web.filter.authc.AnonymousFilter 匿名过滤器
authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter 如果继续操作,需要做对应的表单验证否则不能通过
authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter 基本http验证过滤,如果不通过,跳转屋登录页面
logout org.apache.shiro.web.filter.authc.LogoutFilter 登录退出过滤器
noSessionCreation org.apache.shiro.web.filter.session.NoSessionCreationFilter 没有session创建过滤器
perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter 权限过滤器
port org.apache.shiro.web.filter.authz.PortFilter 端口过滤器,可以设置是否是指定端口如果不是跳转到登录页面
rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter http方法过滤器,可以指定如post不能进行访问等
roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter 角色过滤器,判断当前用户是否指定角色
ssl org.apache.shiro.web.filter.authz.SslFilter 请求需要通过ssl,如果不是跳转回登录页
user org.apache.shiro.web.filter.authc.UserFilter 如果访问一个已知用户,比如记住我功能,走这个过滤器

第四步:自定义Realm 继承AuthorizingRealm 重写  AuthorizationInfo(授权) 和  AuthenticationInfo(认证)

public class CustomRealm extends AuthorizingRealm {
    
    //注入service
    @Autowired
    private SysService sysService;

    // 设置realm的名称
    @Override
    public void setName(String name) {
        super.setName("customRealm");
    }

    // 用于认证
    //没有连接数据库的方法
    /*@Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        
        // token是用户输入的用户名和密码 
        // 第一步从token中取出用户名
        String userCode = (String) token.getPrincipal();

        // 第二步:根据用户输入的userCode从数据库查询
        // ....
    

        // 如果查询不到返回null
        //数据库中用户账号是zhangsansan
        if(!userCode.equals("zhangsansan")){//
            return null;
        }
        
        
        // 模拟从数据库查询到密码
        String password = "111111";

        // 如果查询到返回认证信息AuthenticationInfo
        
        //activeUser就是用户身份信息
        ActiveUser activeUser = new ActiveUser();
        
        activeUser.setUserid("zhangsan");
        activeUser.setUsercode("zhangsan");
        activeUser.setUsername("张三");
        //..
        
        //根据用户id取出菜单
        //通过service取出菜单 
        List<SysPermission> menus  = null;
        try {
            menus = sysService.findMenuListByUserId("zhangsan");
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //将用户菜单 设置到activeUser
        activeUser.setMenus(menus);
        
        
        //将activeUser设置simpleAuthenticationInfo
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                activeUser, password, this.getName());

        return simpleAuthenticationInfo;
    }*/
    
    //realm的认证方法,从数据库查询用户信息
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        
        // token是用户输入的用户名和密码 
        // 第一步从token中取出用户名
        String userCode = (String) token.getPrincipal();

        // 第二步:根据用户输入的userCode从数据库查询
        SysUser sysUser = null;
        try {
            sysUser = sysService.findSysUserByUserCode(userCode);
        } catch (Exception e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        // 如果查询不到返回null
        if(sysUser==null){//
            return null;
        }
        // 从数据库查询到密码
        String password = sysUser.getPassword();
        
        //
        String salt = sysUser.getSalt();

        // 如果查询到返回认证信息AuthenticationInfo
        
        //activeUser就是用户身份信息
        ActiveUser activeUser = new ActiveUser();
        
        activeUser.setUserid(sysUser.getId());
        activeUser.setUsercode(sysUser.getUsercode());
        activeUser.setUsername(sysUser.getUsername());
        //..
        
        //根据用户id取出菜单
        List<SysPermission> menus  = null;
        try {
            //通过service取出菜单 
            menus = sysService.findMenuListByUserId(sysUser.getId());
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //将用户菜单 设置到activeUser
        activeUser.setMenus(menus);

        //将activeUser设置simpleAuthenticationInfo
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                activeUser, password,ByteSource.Util.bytes(salt), this.getName());

        return simpleAuthenticationInfo;
    }
    
    

    // 用于授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        
        //从 principals获取主身份信息
        //将getPrimaryPrincipal方法返回值转为真实身份类型(在上边的doGetAuthenticationInfo认证通过填充到SimpleAuthenticationInfo中身份类型),
        ActiveUser activeUser =  (ActiveUser) principals.getPrimaryPrincipal();
        
        //根据身份信息获取权限信息
        //从数据库获取到权限数据
        List<SysPermission> permissionList = null;
        try {
            permissionList = sysService.findPermissionListByUserId(activeUser.getUserid());
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //单独定一个集合对象 
        List<String> permissions = new ArrayList<String>();
        if(permissionList!=null){
            for(SysPermission sysPermission:permissionList){
                //将数据库中的权限标签 符放入集合
                permissions.add(sysPermission.getPercode());
            }
        }
        
        
    /*    List<String> permissions = new ArrayList<String>();
        permissions.add("user:create");//用户的创建
        permissions.add("item:query");//商品查询权限
        permissions.add("item:add");//商品添加权限
        permissions.add("item:edit");//商品修改权限
*/        //....
        
        //查到权限数据,返回授权信息(要包括 上边的permissions)
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //将上边查询到授权信息填充到simpleAuthorizationInfo对象中
        simpleAuthorizationInfo.addStringPermissions(permissions);

        return simpleAuthorizationInfo;
    }
    
    //清除缓存
    public void clearCached() {
        PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
        super.clearCache(principals);
    }


}

 

第五步:自定义formAuthenticationFilter进行表单验证,目的是在验证用户信息之前先进行验证码验证

package cn.itcast.ssm.shiro;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;

/**
 * 
 * <p>Title: CustomFormAuthenticationFilter</p>
 * <p>Description:自定义FormAuthenticationFilter,认证之前实现 验证码校验 </p>
 * <p>Company: www.itcast.com</p> 
 * @author    
 * @date    2015-3-25下午4:53:15
 * @version 1.0
 */
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {

    //原FormAuthenticationFilter的认证方法
    @Override
    protected boolean onAccessDenied(ServletRequest request,
            ServletResponse response) throws Exception {
        //在这里进行验证码的校验
        
        //从session获取正确验证码
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpSession session =httpServletRequest.getSession();
        //取出session的验证码(正确的验证码)
        String validateCode = (String) session.getAttribute("validateCode");
        
        //取出页面的验证码
        //输入的验证和session中的验证进行对比 
        String randomcode = httpServletRequest.getParameter("randomcode");
        if(randomcode!=null && validateCode!=null && !randomcode.equals(validateCode)){
            //如果校验失败,将验证码错误失败信息,通过shiroLoginFailure设置到request中
            httpServletRequest.setAttribute("shiroLoginFailure", "randomCodeError");
            //拒绝访问,不再校验账号和密码 
            return true; 
        }
        return super.onAccessDenied(request, response);
    }

        
}

第六步:登录页面login.jsp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ include file="/WEB-INF/jsp/tag.jsp"%>
<html>
<head>
<TITLE>药品采购平台</TITLE>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">

<LINK rel="stylesheet" type="text/css" href="${baseurl}styles/style.css">
<LINK rel="stylesheet" type="text/css" href="${baseurl}styles/login.css">
<LINK rel="stylesheet" type="text/css"    href="${baseurl}js/easyui/themes/default/easyui.css">
<LINK rel="stylesheet" type="text/css"    href="${baseurl}js/easyui/themes/icon.css">

<STYLE type="text/css">
.btnalink {
    cursor: hand;
    display: block;
    width: 80px;
    height: 29px;
    float: left;
    margin: 12px 28px 12px auto;
    line-height: 30px;
    background: url('${baseurl}images/login/btnbg.jpg') no-repeat;
    font-size: 14px;
    color: #fff;
    font-weight: bold;
    text-decoration: none;
}
</STYLE>
<%@ include file="/WEB-INF/jsp/common_js.jsp"%>

<script type="text/javascript">

    //登录提示方法
    function loginsubmit() {
        $("#loginform").submit();

    }
    
</SCRIPT>
</HEAD>
<BODY style="background: #f6fdff url(${baseurl}images/login/bg1.jpg) repeat-x;">
    <FORM id="loginform" name="loginform" action="${baseurl}login.action"
        method="post">
        <DIV class="logincon">

            <DIV class="title">
                <IMG alt="" src="${baseurl}images/login/logo.png">
            </DIV>

            <DIV class="cen_con">
                <IMG alt="" src="${baseurl}images/login/bg2.png">
            </DIV>

            <DIV class="tab_con">

                <input type="password" style="display:none;" />
                <TABLE class="tab" border="0" cellSpacing="6" cellPadding="8">
                    <TBODY>
                        <TR>
                            <TD>用户名:</TD>
                            <TD colSpan="2"><input type="text" id="usercode"
                                name="username" style="WIDTH: 130px" /></TD>
                        </TR>
                        <TR>
                            <TD>密 码:</TD>
                            <TD><input type="password" id="pwd" name="password" style="WIDTH: 130px" />
                            </TD>
                        </TR>
                        <TR>
                            <TD>验证码:</TD>
                            <TD><input id="randomcode" name="randomcode" size="8" /> <img
                                id="randomcode_img" src="${baseurl}validatecode.jsp" alt=""
                                width="56" height="20" align='absMiddle' /> <a
                                href=javascript:randomcode_refresh()>刷新</a></TD>
                        </TR>
                        <tr>
                            <TD></TD>
                            <td><input type="checkbox" name="rememberMe" />自动登陆</td>
                        </tr>

                        <TR>
                            <TD colSpan="2" align="center"><input type="button"
                                class="btnalink" onclick="()" value="登&nbsp;&nbsp;录" />
                                <input type="reset" class="btnalink" value="重&nbsp;&nbsp;置" /></TD>
                        </TR>
                    </TBODY>
                </TABLE>

            </DIV>
        </DIV>
    </FORM>
</BODY>
</HTML>

第七步:登录方法login

package cn.itcast.ssm.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import cn.itcast.ssm.exception.CustomException;
import cn.itcast.ssm.service.SysService;

/**
 * 
 * <p>Title: LoginController</p>
 * <p>Description: 登陆和退出</p>
 * <p>Company: www.itcast.com</p> 
 * @author    
 * @date    2015-3-22下午4:43:26
 * @version 1.0
 */
@Controller
public class LoginController {
    
    @Autowired
    private SysService sysService;
    
    
    //用户登陆提交方法
    /**
     * 
     * <p>Title: login</p>
     * <p>Description: </p>
     * @param session
     * @param randomcode 输入的验证码
     * @param usercode 用户账号
     * @param password 用户密码 
     * @return
     * @throws Exception
     */
    /*@RequestMapping("/login")
    public String login(HttpSession session, String randomcode,String usercode,String password)throws Exception{
        
        //校验验证码,防止恶性攻击
        //从session获取正确验证码
        String validateCode = (String) session.getAttribute("validateCode");
        
        //输入的验证和session中的验证进行对比 
        if(!randomcode.equals(validateCode)){
            //抛出异常
            throw new CustomException("验证码输入错误");
        }
        
        //调用service校验用户账号和密码的正确性
        ActiveUser activeUser = sysService.authenticat(usercode, password);
        
        //如果service校验通过,将用户身份记录到session
        session.setAttribute("activeUser", activeUser);
        //重定向到商品查询页面
        return "redirect:/first.action";
    }*/
    
    //登陆提交地址,和applicationContext-shiro.xml中配置的loginurl一致
    @RequestMapping("login")
    public String login(HttpServletRequest request)throws Exception{
        
        //如果登陆失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名
        String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
        //根据shiro返回的异常类路径判断,抛出指定异常信息
        if(exceptionClassName!=null){
            if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
                //最终会抛给异常处理器
                throw new CustomException("账号不存在");
            } else if (IncorrectCredentialsException.class.getName().equals(
                    exceptionClassName)) {
                throw new CustomException("用户名/密码错误");
            } else if("randomCodeError".equals(exceptionClassName)){
                throw new CustomException("验证码错误 ");
            }else {
                throw new Exception();//最终在异常处理器生成未知错误
            }
        }
        //此方法不处理登陆成功(认证成功),shiro认证成功会自动跳转到上一个请求路径
        //登陆失败还到login页面
        return "login";
    }
    
/*    //用户退出
    @RequestMapping("/logout")
    public String logout(HttpSession session)throws Exception{
        
        //session失效
        session.invalidate();
        //重定向到商品查询页面
        return "redirect:/first.action";
        
    }*/
    

}

第八步:系统首页方法first.action

package cn.itcast.ssm.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import cn.itcast.ssm.po.ActiveUser;


@Controller
public class FirstAction {
    //系统首页
    @RequestMapping("/first")
    public String first(Model model)throws Exception{
        
        //从shiro的session中取activeUser
        Subject subject = SecurityUtils.getSubject();
        //取身份信息
        ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
        //通过model传到页面
        model.addAttribute("activeUser", activeUser);
        
        return "/first";
    }
    
    //欢迎页面
    @RequestMapping("/welcome")
    public String welcome(Model model)throws Exception{
        
        return "/welcome";
        
    }
}    

第九步:系统首页及权限页面first.jsp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ include file="/WEB-INF/jsp/tag.jsp"%>
<html>
<head>
<title>药品采购平台</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">

<LINK rel="stylesheet" type="text/css" href="${baseurl}js/easyui/styles/default.css">
<%@ include file="/WEB-INF/jsp/common_css.jsp"%>
<%@ include file="/WEB-INF/jsp/common_js.jsp"%>
<SCRIPT type="text/javascript">
    var tabOnSelect = function(title) {
        //根据标题获取tab对象
        var currTab = $('#tabs').tabs('getTab', title);
        var iframe = $(currTab.panel('options').content);//获取标签的内容
        
        var src = iframe.attr('src');//获取iframe的src
        //当重新选中tab时将ifram的内容重新加载一遍,目的是获取当前页面的最新内容
        if (src)
            $('#tabs').tabs('update', {
                tab : currTab,
                options : {
                    content : createFrame(src)//ifram内容
                }
            });

    };
    var _menus;
    $(function() {//预加载方法
        //通过ajax请求菜单
        /* $.ajax({
            url : '${baseurl}menu.json',
            type : 'POST',
            dataType : 'json',
            success : function(data) {
                _menus = data;
                initMenu(_menus);//解析json数据,将菜单生成
            },
            error : function(msg) {
                alert('菜单加载异常!');
            }
        }); */

        //tabClose();
        //tabCloseEven();

        $('#tabs').tabs('add', {
            title : '欢迎使用',
            content : createFrame('${baseurl}welcome.jsp')
        }).tabs({
            //当重新选中tab时将ifram的内容重新加载一遍
            onSelect : tabOnSelect
        });
        
        //修改密码
        $('#modifypwd').click(menuclick);

    });

    //退出系统方法
    function logout() {
        _confirm('您确定要退出本系统吗?',null,
                function(){
                    location.href = '${baseurl}logout.action';
                }
        )
    }
    

    //帮助
    function showhelp(){
        window.open('${baseurl}help/help.html','帮助文档'); 
    }
    
    
</SCRIPT>



<META name="GENERATOR" content="MSHTML 9.00.8112.16540">
</HEAD>

<BODY style="overflow-y: hidden;" class="easyui-layout" scroll="no" >
    <DIV
        style='background: url("images/layout-browser-hd-bg.gif") repeat-x center 50% rgb(127, 153, 190); height: 30px; color: rgb(255, 255, 255); line-height: 20px; overflow: hidden; font-family: Verdana, 微软雅黑, 黑体;'
        border="false" split="true" region="north">
        <SPAN style="padding-right: 20px; float: right;" class="head">
            欢迎当前用户:${activeUser.username}&nbsp;&nbsp;
            <A href=javascript:showhelp()>使用帮助</A>
            &nbsp;&nbsp;
            <A title='修改密码' ref='modifypwd' href="#" rel='${baseurl}user/updatepwd.action' icon='icon-null' id="modifypwd" >修改密码</A>
            &nbsp;&nbsp;
            <A id="loginOut" href=javascript:logout()>退出系统</A>

        </SPAN> <SPAN style="padding-left: 10px; font-size: 16px;"><IMG
            align="absmiddle" src="images/blocks.gif" width="20" height="20">
            医药集中采购系统</SPAN> <SPAN style="padding-left: 15px;" id="News"></SPAN>
    </DIV>

    <DIV style="background: rgb(210, 224, 242); height: 30px;" split="false"
        region="south">

        <DIV class="footer">
            系统版本号:${version_number}&nbsp;&nbsp;&nbsp;发布日期:${version_date}
        </DIV>
    </DIV>

    <DIV style="width: 180px;" id="west" title="导航菜单" split="true"
        region="west" hide="true">

        <DIV id="nav" class="easyui-accordion" border="false" fit="true">
            
            <c:if test="${activeUser.menus!=null }">
                <ul>
                <c:forEach items="${activeUser.menus }" var="menu">
                    <li><div>
                        <a title="${menu.name }" ref="1_1" href="#"
                            rel="${baseurl }/${menu.url }" icon="icon-log"><span
                            class="icon icon-log">&nbsp;</span><span class="nav"><a href=javascript:addTab('${menu.name }','${baseurl }/${menu.url }')>${menu.name }</a></span></a>
                    </div></li>
                </c:forEach>
                </ul>
            </c:if>
            <%-- <ul>
            <li><div>
                    <a title="创建采购单" ref="1_1" href="#"
                        rel="${baseurl} items/queryItems.action" icon="icon-log"><span
                        class="icon icon-log">&nbsp;</span><span class="nav"><a href=javascript:addTab('创建采购单','${baseurl}items/queryItems.action')>商品查询</a></span></a>
                </div></li>
            <li><div>
                    <a title="提交采购单" ref="1_1" href="#"
                        rel="/purchasing/order/orderList.action?type=1" icon="icon-log"><span
                        class="icon icon-log">&nbsp;</span><span class="nav">提交采购单</span></a>
                </div></li>
            <li><div>
                    <a title="部门经理审核" ref="1_1" href="#"
                        rel="/purchasing/order/orderList.action?type=2" icon="icon-log"><span
                        class="icon icon-log">&nbsp;</span><span class="nav">部门经理审核</span></a>
                </div></li>
            <li><div>
                    <a title="总经理审核" ref="1_1" href="#"
                        rel="/purchasing/order/orderList.action?type=3" icon="icon-log"><span
                        class="icon icon-log">&nbsp;</span><span class="nav">总经理审核</span></a>
                </div></li>
        </ul> --%>
        </DIV>
    </DIV>

    <DIV style="background: rgb(238, 238, 238); overflow-y: hidden;"
        id="mainPanle" region="center">
        <DIV id="tabs" class="easyui-tabs" border="false" fit="true"></DIV>
    </DIV>


</BODY>
</HTML>

 

附 验证码jsp

validatecode.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="java.util.Random"%>
<%@ page import="java.io.OutputStream"%>
<%@ page import="java.awt.Color"%>
<%@ page import="java.awt.Font"%>
<%@ page import="java.awt.Graphics"%>
<%@ page import="java.awt.image.BufferedImage"%>
<%@ page import="javax.imageio.ImageIO"%>
<%
    int width = 60;
    int height = 32;
    //create the image
    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    Graphics g = image.getGraphics();
    // set the background color
    g.setColor(new Color(0xDCDCDC));
    g.fillRect(0, 0, width, height);
    // draw the border
    g.setColor(Color.black);
    g.drawRect(0, 0, width - 1, height - 1);
    // create a random instance to generate the codes
    Random rdm = new Random();
    String hash1 = Integer.toHexString(rdm.nextInt());
    System.out.print(hash1);
    // make some confusion
    for (int i = 0; i < 50; i++) {
        int x = rdm.nextInt(width);
        int y = rdm.nextInt(height);
        g.drawOval(x, y, 0, 0);
    }
    // generate a random code
    String capstr = hash1.substring(0, 4);
    //将生成的验证码存入session
    session.setAttribute("validateCode", capstr);
    g.setColor(new Color(0, 100, 0));
    g.setFont(new Font("Candara", Font.BOLD, 24));
    g.drawString(capstr, 8, 24);
    g.dispose();
    //输出图片
    response.setContentType("image/jpeg");
    out.clear();
    out = pageContext.pushBody();
    OutputStream strm = response.getOutputStream();
    ImageIO.write(image, "jpeg", strm);
    strm.close();
%>

 

posted @ 2018-06-21 17:36  十月围城小童鞋  阅读(249)  评论(0编辑  收藏  举报