springboot整合shiro實(shí)現(xiàn)記住我功能
前言
上一篇 文章我們完成了在 thymeleaf 模板引擎中使用 shiro 標(biāo)簽,也就是根據(jù)不同的用戶身份信息,前端頁(yè)面來(lái)顯示不同的頁(yè)面內(nèi)容。本篇文章我們來(lái)完成在登錄頁(yè)面的記住我的功能
springboot 整合 shiro 之實(shí)現(xiàn)記住我
項(xiàng)目依然使用 springboot整合shiro 這個(gè)項(xiàng)目,稍稍改動(dòng)即可完成記住我的功能
配置類 ShiroConfig
完整的代碼如下
@Configuration
public class ShiroConfig {
/**
* 安全管理器
*
* @param userRealm userRealm
* @return defaultWebSecurityManager
*/
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(UserRealm userRealm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(userRealm);
// 實(shí)現(xiàn)記住我,所需要的配置
defaultWebSecurityManager.setRememberMeManager(cookieRememberMeManager());
return defaultWebSecurityManager;
}
/**
* thymeleaf模板引擎中使用shiro標(biāo)簽時(shí),要用到
*
* @return
*/
@Bean
public ShiroDialect getShiroDialect() {
return new ShiroDialect();
}
@Bean
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
// 設(shè)置登錄頁(yè)面url
shiroFilterFactoryBean.setLoginUrl("/user/login");
shiroFilterFactoryBean.setSuccessUrl("/user/index");
shiroFilterFactoryBean.setUnauthorizedUrl("/user/unauthorized");
// 注意此處使用的是LinkedHashMap是有順序的,shiro會(huì)按從上到下的順序匹配驗(yàn)證,匹配了就不再繼續(xù)驗(yàn)證
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/layer/**", "anon");// 靜態(tài)資源放行
filterChainDefinitionMap.put("/img/**", "anon");
filterChainDefinitionMap.put("/jquery/**", "anon");
// add.html頁(yè)面放行
filterChainDefinitionMap.put("/user/add", "authc");
// update.html必須認(rèn)證
filterChainDefinitionMap.put("/user/update", "authc");
// index.html必須認(rèn)證
filterChainDefinitionMap.put("/user/index", "user");
// 設(shè)置授權(quán),只有user:add權(quán)限的才能請(qǐng)求/user/add這個(gè)url
filterChainDefinitionMap.put("/user/add", "perms[user:add]");
filterChainDefinitionMap.put("/user/update", "perms[user:update]");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
// 實(shí)現(xiàn)記住我,所需要的配置
@Bean
public SimpleCookie simpleCookie() {
// 這個(gè)參數(shù)是cookie的名稱,對(duì)應(yīng)前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
simpleCookie.setHttpOnly(true);
// 記住我cookie生效時(shí)間1小時(shí),單位秒
simpleCookie.setMaxAge(60 * 60);
return simpleCookie;
}
// 實(shí)現(xiàn)記住我,所需要的配置
@Bean
public CookieRememberMeManager cookieRememberMeManager() {
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(simpleCookie());
// rememberMe cookie加密的密鑰 建議每個(gè)項(xiàng)目都不一樣 默認(rèn)AES算法 密鑰長(zhǎng)度(128 256 512 位)
cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
return cookieRememberMeManager;
}
}
login.html 登錄頁(yè)面
此時(shí)要拿到復(fù)選框 checkbox 是否被用戶選中的狀態(tài)值,選中為 true,未選中為 false,將這個(gè)狀態(tài)值發(fā)生至后端登錄接口
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登錄</title>
<link rel="shortcut icon" type="image/x-icon" th:href="@{/img/favicon.ico}"/>
</head>
<body>
<form action="" method="post">
<p>
賬號(hào):
<label><input type="text" class="username" name="username"></label>
</p>
<p>
密碼:
<label><input type="text" class="password" name="password"></label>
</p>
<p>
<label><input id="checkbox1" type="checkbox" name="rememberMe"></label>記住我
</p>
<p><button type="button" class="loginBtn">登錄</button></p>
</form>
</body>
<script type="text/javascript" th:src="@{/jquery/jquery-3.3.1.min.js}"></script>
<script type="text/javascript" th:src="@{/layer/layer.js}"></script><!--layui的彈出層-->
<script type="text/javascript">
$(document).ready(function () {
$('.loginBtn').on('click', function () { // 登錄按鈕
const username = $('.username').val();
const password = $('.password').val();
const rememberMe = $("input[type='checkbox']").is(':checked');
$.ajax({// 用戶登錄
type: 'post',
url: '/user/doLogin',
dataType: 'json',
data: ({
'username': username,
'password': password,
'rememberMe': rememberMe
}),
success: function (resp) {
console.log(resp);
if (resp.code !== 200) {
layer.msg(resp.message, function () {// layui的彈窗
});
} else if (resp.code === 200) {
window.location.+ resp.action;
}
},
error: function () {// 此處添加錯(cuò)誤處理
layer.open({
title: '提示信息',
content: '后臺(tái)訪問(wèn)錯(cuò)誤,請(qǐng)聯(lián)系管理員',
skin: 'layui-layer-molv',
icon: 0
});
}
});
});
});
</script>
</html>
controller
@Controller
@RequestMapping(path = "/user")
@Slf4j
public class UserController {
@GetMapping(path = "/login")
public String login() {
return "login";
}
@GetMapping(path = "/index")
public String index() {
return "index";
}
@GetMapping(path = "/add")
public String add() {
return "add";
}
@GetMapping(path = "/update")
public String update() {
return "update";
}
@GetMapping(path = "/unauthorized")
public String unauthorized() {
return "unauthorized";
}
/**
* 用戶登錄
*
* @param userVO
* @param bindingResult
* @return
*/
@PostMapping(path = "/doLogin")
@ResponseBody
public ResultMap doLogin(@NotNull @Valid UserVO userVO, @NotNull BindingResult bindingResult) {
// ------參數(shù)校驗(yàn)------
if (bindingResult.hasErrors()) {
String message = Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage();
log.info("校驗(yàn)的message信息為:" + message);
return new ResultMap().fail().message(message);
}
// 將用戶名,密碼交給shiro
UsernamePasswordToken token = new UsernamePasswordToken(userVO.getUsername(), userVO.getPassword(), userVO.getRememberMe());
String msg;
try {
// shiro幫我們匹配密碼什么的,我們只需要把東西傳給它,它會(huì)根據(jù)我們?cè)赨serRealm里認(rèn)證方法設(shè)置的來(lái)驗(yàn)證
Subject subject = SecurityUtils.getSubject();
subject.login(token);
return new ResultMap().success().action("/user/index");
} catch (AuthenticationException e) {
if (e instanceof IncorrectCredentialsException) {
msg = "密碼錯(cuò)誤";
} else if (e instanceof LockedAccountException) {
msg = "用戶被禁用";
} else if (e instanceof UnknownAccountException) {
msg = "用戶不存在";
} else {
msg = "用戶認(rèn)證失敗";
}
}
return new ResultMap().error().message(msg);
}
/**
* 用戶退出登錄
* 添加記住我功能了,退出登錄時(shí),除了要當(dāng)前的subject退出之外,還要?jiǎng)h除用戶瀏覽器上的Cookie信息
*
* @return
*/
@GetMapping(path = "/logout")
public String logout(HttpServletResponse response) {
Subject subject = SecurityUtils.getSubject();
if (subject.isAuthenticated()) {
subject.logout();
Cookie cookie = new Cookie("rememberMe", null);
cookie.setMaxAge(0);
response.addCookie(cookie);
}
return "login";
}
}
UserVO 類
public class UserVO implements Serializable {
@NotBlank(message = "賬號(hào)不能為空")
private String username;
@NotEmpty(message = "密碼不能為空")
private String password;
private Boolean rememberMe;
// 省略set/get方法
}
測(cè)試
我們以賬號(hào) jack 為例進(jìn)行登錄,如下

進(jìn)入首頁(yè)頁(yè)面如下,再次查看 Cookies 數(shù)據(jù)

我們這時(shí)關(guān)閉這個(gè)首頁(yè)頁(yè)面,在瀏覽器地址欄輸入 http://127.0.0.1:8080/user/index 再次進(jìn)入首頁(yè)頁(yè)面,會(huì)發(fā)現(xiàn)如上圖一樣,可以順利訪問(wèn),說(shuō)明我們的記住我功能已經(jīng)實(shí)現(xiàn)。這時(shí),可以再次在瀏覽器地址欄輸入 http://127.0.0.1:8080/user/add,進(jìn)入 add.html 頁(yè)面,如下

在 Cookies 的有效期內(nèi),當(dāng)你關(guān)閉瀏覽器之后,再次進(jìn)入 add.html 頁(yè)面時(shí),無(wú)需登錄直接就可以訪問(wèn)了,說(shuō)明記住我功能已經(jīng)實(shí)現(xiàn)了。在瀏覽器地址欄輸入 http://127.0.0.1:8080/user/update,進(jìn)入 update.html 頁(yè)面,如下

說(shuō)明賬號(hào) jack 沒(méi)有權(quán)限訪問(wèn) update.html 頁(yè)面,可以看控制臺(tái) sql 日志

到此這篇關(guān)于springboot整合shiro之實(shí)現(xiàn)記住我的文章就介紹到這了,更多相關(guān)springboot整合shiro之實(shí)現(xiàn)記住我內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談Mybatis+mysql 存儲(chǔ)Date類型的坑
這篇文章主要介紹了淺談Mybatis+mysql 存儲(chǔ)Date類型的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-11-11
Java 生成任意長(zhǎng)度的驗(yàn)證碼過(guò)程解析
這篇文章主要介紹了Java 生成任意長(zhǎng)度的驗(yàn)證碼過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
利用Java的Struts框架實(shí)現(xiàn)電子郵件發(fā)送功能
這篇文章主要介紹了利用Java的Struts框架實(shí)現(xiàn)電子郵件發(fā)送功能,Struts框架是Java的SSH三大web開(kāi)發(fā)框架之一,需要的朋友可以參考下2015-12-12
JAVA中@ApiModel和@ApiModelProperty注解實(shí)戰(zhàn)代碼
這篇文章主要給大家介紹了關(guān)于JAVA中@ApiModel和@ApiModelProperty注解的相關(guān)資料,@ApiModel注解是用在接口相關(guān)的實(shí)體類上的注解,它主要是用來(lái)對(duì)使用該注解的接口相關(guān)的實(shí)體類添加額外的描述信息,常常和@ApiModelProperty注解配合使用,需要的朋友可以參考下2024-03-03
Mybatis動(dòng)態(tài)調(diào)用表名和字段名的解決方法
今天在項(xiàng)目開(kāi)發(fā)中有個(gè)業(yè)務(wù)是需要限制各個(gè)用戶對(duì)某些表里的字段查詢以及某些字段是否顯示,這種情況下,就需要構(gòu)建sql來(lái)動(dòng)態(tài)傳入表名、字段名了,下面給大家介紹mybatis動(dòng)態(tài)調(diào)用表名和字段名的解決方法,一起看看吧2016-10-10
Java文件斷點(diǎn)續(xù)傳實(shí)現(xiàn)原理解析
這篇文章主要介紹了Java文件斷點(diǎn)續(xù)傳實(shí)現(xiàn)原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05
Spring Boot實(shí)現(xiàn)圖片上傳/加水印一把梭操作實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于Spring Boot實(shí)現(xiàn)圖片上傳/加水印一把梭操作的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11

