1. 程式人生 > >shiro登入驗證(登入跳轉到指定頁面,驗證碼驗證,不登出之前已登入使用者下,再次登入)

shiro登入驗證(登入跳轉到指定頁面,驗證碼驗證,不登出之前已登入使用者下,再次登入)

web.xml配置

    <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>

shiro的配置檔案(bean-shiro.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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"
>
<!-- 自定義表單認證過濾器 com.panda.contorller.shiro.MyFormAuthenticationFilter(見後面)--> <bean id="myFormAuthenticationFilter" class="com.panda.contorller.shiro.MyFormAuthenticationFilter"> <property name="usernameParam" value="username"></property> <property name="passwordParam" value="password"></property> <property name="rememberMeParam" value="rememberMe"></property> </bean> <!-- shiro過濾器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="loginUrl" value="/login"></property> <property name="unauthorizedUrl" value="/refuse.jsp"></property> <property name="successUrl" value="/index"></property> <property name="securityManager" ref="securityManager"></property> <!-- 自定義filter --> <property name="filters"> <map> <entry key="authc" value-ref="myFormAuthenticationFilter" /> </map> </property> <property name="filterChainDefinitions"> <value> /logout.action=logout /images/**=anon /js/**=anon /script/**=anon /style/**=anon /themes/**=anon /charts/**=anon <!-- /login.jsp=anon --> /images/image.jsp=anon /**=authc </value> </property> </bean> <!-- 配置安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="customRealm"></property> </bean> <!-- 配置realm --> <bean id="customRealm" class="com.panda.service.realm.CustomRealm"> <!-- <property name="credentialsMatcher" ref="credentialsMatcher"></property> --> </bean> <!-- 配置憑證匹配器,md5加密 --> <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <property name="hashAlgorithmName" value="md5"></property> <property name="hashIterations" value="1"></property> </bean> </beans>

一個簡單的驗證碼生成(jsp頁面)

<%@ page language="java" contentType="text/html; charset=UTF-8" import="java.awt.*, java.awt.image.*,java.util.*,javax.imageio.*"
    pageEncoding="UTF-8"%>
<%!Color getRandColor(int fc, int bc) {
        Random random = new Random();
        if (fc > 255)
            fc = 255;
        if (bc > 255)
            bc = 255;
        int r = fc + random.nextInt(bc - fc);
        int g = fc + random.nextInt(bc - fc);
        int b = fc + random.nextInt(bc - fc);
        return new Color(r, g, b);
    }%>
<%
    out.clear();
    response.setHeader("Pragma", "No-cache");
    response.setHeader("Cache-Control", "no-cache");
    response.setDateHeader("Expires", 0);
    int width = 60, height = 20;
    BufferedImage image = new BufferedImage(width, height,
            BufferedImage.TYPE_INT_RGB);
    Graphics g = image.getGraphics();
    Random random = new Random();
    g.setColor(getRandColor(200, 250));
    g.fillRect(0, 0, width, height);
    g.setFont(new Font("Times New Roman", Font.PLAIN, 18));
    g.setColor(getRandColor(160, 200));
    for (int i = 0; i < 155; i++) {
        int x = random.nextInt(width);
        int y = random.nextInt(height);
        int xl = random.nextInt(12);
        int yl = random.nextInt(12);
        g.drawLine(x, y, x + xl, y + yl);
    }
    String sRand = "";
    for (int i = 0; i < 4; i++) {
        String rand = String.valueOf(random.nextInt(10));
        sRand += rand;
        g.setColor(new Color(20 + random.nextInt(110), 20 + random
                .nextInt(110), 20 + random.nextInt(110)));
        g.drawString(rand, 13 * i + 6, 16);
    }
    // 將驗證碼存入SESSION
    session.setAttribute("sRand", sRand);
    g.dispose();
    ImageIO.write(image, "JPEG", response.getOutputStream());
%>

from表單

<form  id="loginForm" action="${ctx}/login" method="post" >
使用者名稱:
<input type="text" name="username" value="123" style="background-color: white;border: 1px solid #a9a9a9" id="userName" size="20" maxlength="20">

密碼:
<input name="password" id="pwd" value="123" style="background-color: white;border: 1px solid #a9a9a9" type="password" size="20" maxlength="20">

驗證碼:
<input type="text" value="1"  style="background-color: white;border: 1px solid #a9a9a9" name="imageCode" class="txtCode" id="imageCode" size="10" />&nbsp; &nbsp; 

<img onclick="javascript:loadimage();" title="換一張試試" name="randImage" id="randImage" src="${ctx}/images/image.jsp" width="60" height="20" border="1" align="absmiddle">
</form>

登入的controller

package com.panda.contorller;

import java.util.List;

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

/*import org.apache.commons.lang3.StringUtils;*/
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
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 com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;
import com.panda.pojo.Menu;
import com.panda.pojo.MenuAndPower;
import com.panda.pojo.User;
import com.panda.service.impl.MenuService;

@Controller
public class IndexController {
    @Autowired
    private MenuService menuService = null;
    /**
     * 登陸成功後進入這裡,獲取使用者資訊和選單許可權
     * @param model
     * @param session
     * @return
     */
    @RequestMapping("/index")
    public String first(Model model,HttpSession session) {
        Subject subject = SecurityUtils.getSubject();//第一步:獲取我們的主體
        User user = (User) subject.getPrincipal();
        session.setAttribute("userId",user.getUser_id());
        model.addAttribute("userActive", user); //將資料放置到域物件
        //選單轉化為自定義的json
        SimplePropertyPreFilter filter = new SimplePropertyPreFilter(MenuAndPower.class, "id","pId","text","url");     
        String json = JSON.toJSONString(user.getMenuAndPowers(), filter);
        model.addAttribute("menu", json);
        return "main2.jsp";

    }

    /**
     * 登陸失敗的處理
     * @param request
     * @param session
     * @return
     */
    @RequestMapping("/login")
    public String login(HttpServletRequest request,HttpSession session) {
        // 獲取我們的錯誤資訊
        String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
        //方法一
        /*if(UnknownAccountException.class.getName().equals(exceptionClassName)) {
            request.setAttribute("logError", "使用者名稱不存在");
        }*/
        //方法二時
        if(null!=exceptionClassName){
            if(exceptionClassName.equals("randomCodeError")) {
                request.setAttribute("logError", "驗證碼錯誤!");
                return "login.jsp";
            }
            if(exceptionClassName.equals("org.apache.shiro.authc.UnknownAccountException")||exceptionClassName.equals("org.apache.shiro.authc.IncorrectCredentialsException")){
                request.setAttribute("logError", "使用者名稱或密碼錯誤");
            }
        }
        return "login.jsp";

    }

}

自定義的realm,認證登入資訊

package com.panda.service.realm;

import java.util.ArrayList;
import java.util.List;

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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.stereotype.Service;

import com.panda.pojo.MenuAndPower;
import com.panda.pojo.User;
import com.panda.repository.MenuAndPowerRepository;
import com.panda.repository.UserRepository;

@Service
public class CustomRealm extends AuthorizingRealm {

    @Override
    public String getName() {
        return super.getName();
    }

    @Resource
    private MenuAndPowerRepository menuAndPowerRepository = null;

    @Resource
    private UserRepository userRepository = null;

    /**
     * 授權
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

    return null;
    }

    /**
     * 認證
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();//獲取表單的使用者名稱
        User user=null;
        try {
            user = userRepository.queryByUsername(username);//從資料庫查詢使用者
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        if (user == null) {
            return null;
        }

        User userActive = new User();
        List<MenuAndPower> menus = new ArrayList<>();
        try {
            menus = menuAndPowerRepository.queryMenuByUserId(user.getUser_id());//獲取該使用者的選單
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //將使用者資訊和選單放入物件
        userActive.setUser_id(user.getUser_id());
        userActive.setUser_name(username);
        userActive.setMenuAndPowers(menus);
        //System.out.println(menus);
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userActive, user.getPassword(),
                getName());
        return simpleAuthenticationInfo;
    }

}

繼承shiro的FormAuthenticationFilter類 ,重寫裡面的一些方法。實現shiro登入跳轉到指定頁面,驗證碼的實現,不登出之前已登入使用者下,重新登入

MyFormAuthenticationFilter類

package com.panda.contorller.shiro;

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

import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;

import com.panda.pojo.User;

public class MyFormAuthenticationFilter extends FormAuthenticationFilter {

    /**
     * 每次登入都會到這裡來,這裡用來處理 不登出之前已登入使用者下,再次登入
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
         if (isLoginRequest(request, response))
            {
                if (isLoginSubmission(request, response))
                {
                    //本次使用者登陸賬號
                    String account = this.getUsername(request);

                    Subject subject = this.getSubject(request, response);
                    //之前登陸的使用者
                    User user = (User) subject.getPrincipal();
                    //如果兩次登陸的使用者不一樣,則先退出之前登陸的使用者,(有問題,相同使用者無法跳轉頁面)解決:可以不判斷,都退出之前的登入,再重新登入
                    if (account != null && user != null && !account.equals(user.getUser_name()))
                    {
                        //獲取session,獲取驗證碼
                        HttpServletRequest httpServletRequest=(HttpServletRequest) request;
                        HttpSession session= httpServletRequest.getSession();
                        String sRand = (String) session.getAttribute("sRand");
                        //登出登入,同時會使session失效
                        subject.logout();
                        //所以重新設定session
                        HttpSession session1= httpServletRequest.getSession();
                        session1.setAttribute("sRand", sRand);
                    }
                }
            }

        return super.isAccessAllowed(request, response, mappedValue);
    }

    /**
     * 重寫FormAuthenticationFilter的onLoginSuccess方法
     * 指定的url傳遞進去,這樣就實現了跳轉到指定的頁面;
     */
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request,
            ServletResponse response) throws Exception {
        WebUtils.getAndClearSavedRequest(request);//清理了session中儲存的請求資訊
        WebUtils.redirectToSavedRequest(request, response, getSuccessUrl());
        return false;
    }

    /**
     * 驗證碼驗證
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest=(HttpServletRequest) request;
        //從session獲取驗證碼,正確的驗證碼
        HttpSession session=httpServletRequest.getSession();
        String validate =(String) session.getAttribute("sRand");
        //獲取輸入的驗證碼
        String myValidate = httpServletRequest.getParameter("imageCode");
        //驗證失敗,設定錯誤資訊
        if (validate!=null&& myValidate!=null&&!validate.equals(myValidate)) {
            httpServletRequest.setAttribute("shiroLoginFailure", "randomCodeError");
            //拒絕訪問
            return true;
        }
        return super.onAccessDenied(request, response);
    }
}