Spring Boot Shiro許可權管理--自定義 FormAuthenticationFilter驗證碼整合
阿新 • • 發佈:2018-12-29
驗證碼
思路
shiro使用FormAuthenticationFilter進行表單認證,驗證校驗的功能應該加在FormAuthenticationFilter中,在認證之前進行驗證碼校驗。
需要寫FormAuthenticationFilter的子類,繼承FormAuthenticationFilter,改寫它的認證方法,在認證之前進行驗證碼校驗。
在ShiroConfiguration.java中的shiroFilter方法注入自定義FormAuthenticationFilter
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();//獲取filters
filters.put("authc", new CustomFormAuthenticationFilter());//將自定義 的FormAuthenticationFilter注入shiroFilter中
驗證碼Servlet
Application.java
思路
shiro使用FormAuthenticationFilter進行表單認證,驗證校驗的功能應該加在FormAuthenticationFilter中,在認證之前進行驗證碼校驗。
需要寫FormAuthenticationFilter的子類,繼承FormAuthenticationFilter,改寫它的認證方法,在認證之前進行驗證碼校驗。
自定義FormAuthenticationFilter
package com.example.config.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; public class CustomFormAuthenticationFilter extends FormAuthenticationFilter{ @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { // 在這裡進行驗證碼的校驗 HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpSession session = httpServletRequest.getSession(); // 取出驗證碼 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", "kaptchaValidateFailed");//自定義登入異常 // 拒絕訪問,不再校驗賬號和密碼 return true; } return super.onAccessDenied(request, response); } }
在ShiroConfiguration.java中的shiroFilter方法注入自定義FormAuthenticationFilter
@Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { System.out.println("ShiroConfiguration.shiroFilter()"); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();//獲取filters filters.put("authc", new CustomFormAuthenticationFilter());//將自定義 的FormAuthenticationFilter注入shiroFilter中 // 必須設定SecuritManager shiroFilterFactoryBean.setSecurityManager(securityManager); // 攔截器 Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); // 配置退出過濾器,其中的具體程式碼Shiro已經替我們實現了 filterChainDefinitionMap.put("/logout", "logout"); //驗證碼可以匿名訪問 filterChainDefinitionMap.put("/validatecodeServlet", "anon"); //配置記住我或認證通過可以訪問的地址 filterChainDefinitionMap.put("/index", "user"); filterChainDefinitionMap.put("/", "user"); // <!-- 過濾鏈定義,從上向下順序執行,一般將 /**放在最為下邊 -->:這是一個坑呢,一不小心程式碼就不好使了; // <!-- authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問--> filterChainDefinitionMap.put("/**", "authc"); // 如果不設定預設會自動尋找Web工程根目錄下的"/login.jsp"頁面 shiroFilterFactoryBean.setLoginUrl("/login"); // 登入成功後要跳轉的連結 shiroFilterFactoryBean.setSuccessUrl("/index"); // 未授權介面; shiroFilterFactoryBean.setUnauthorizedUrl("/403"); shiroFilterFactoryBean .setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; }
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();//獲取filters
filters.put("authc", new CustomFormAuthenticationFilter());//將自定義 的FormAuthenticationFilter注入shiroFilter中
登入方法加入自定義的異常kaptchaValidateFailed
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(HttpServletRequest request, Map<String, Object> map) {
System.out.println("HomeController.login");
// 登入失敗從request中獲取shiro處理的異常資訊
// shiroLoginFailure:就是shiro異常類的全類名
String exception = (String) request.getAttribute("shiroLoginFailure");
String msg = "";
if (exception != null) {
if (UnknownAccountException.class.getName().equals(exception)) {
System.out.println("UnknownAccountException -->帳號不存在:");
msg = "UnknownAccountException -->帳號不存在:";
} else if (IncorrectCredentialsException.class.getName().equals(exception)) {
System.out.println("IncorrectCredentialsException -- > 密碼不正確:");
msg = "IncorrectCredentialsException -- > 密碼不正確:";
} else if ("kaptchaValidateFailed".equals(exception)) {
System.out.println("kaptchaValidateFailed -- > 驗證碼錯誤");
msg = "kaptchaValidateFailed -- > 驗證碼錯誤";
} else {
msg = "else >> " + exception;
System.out.println("else -- >" + exception);
}
}
map.put("msg", msg);
// 此方法不處理登入成功,由shiro進行處理.
return "/login";
}
login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<meta charset="UTF-8" />
<title>Insert title here</title>
</head>
<body>
錯誤資訊:<h4 th:text="${msg}"></h4>
<form action="" method="post">
<p>賬號:<input type="text" name="username" value="admin"/></p>
<p>密碼:<input type="text" name="password" value="123456"/></p>
<p>驗證碼:<input type="text" name="randomcode"/>
<img th:src="@{/validatecodeServlet}" height="20px" width="60px" onclick="random(this)"/></p>
<P><input type="checkbox" name="rememberMe" />記住我</P>
<p><input type="submit" value="登入"/></p>
</form>
<script th:inline="javascript">
function random(tmp){
tmp.src="/validatecodeServlet?rnd="+Math.random();
}
</script>
</body>
</html>
驗證碼Servlet
package com.example.servlet;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@WebServlet(urlPatterns="/validatecodeServlet")
public class ValidatecodeServlet extends HttpServlet{
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(">>>>>>>>>>doGet()<<<<<<<<<<<");
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(">>>>>>>>>>doPost()<<<<<<<<<<<");
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);
HttpSession session = req.getSession(true);
//將生成的驗證碼存入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();
//輸出圖片
resp.setContentType("image/jpeg");
OutputStream strm = resp.getOutputStream();
ImageIO.write(image, "jpeg", strm);
strm.close();
}
}
Application.java
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@ServletComponentScan
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}