shiro登入驗證(登入跳轉到指定頁面,驗證碼驗證,不登出之前已登入使用者下,再次登入)
阿新 • • 發佈:2019-02-19
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" />
<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);
}
}