SpringBoot如何實(shí)現(xiàn)持久化登錄狀態(tài)獲取
SpringBoot 持久化登錄狀態(tài)獲取
1.編寫(xiě)登錄的controller文件
寫(xiě)入cookie
//登陸成功后
//...將用戶賬號(hào)信息存入數(shù)據(jù)庫(kù)中
//寫(xiě)cookie,(因存入數(shù)據(jù)庫(kù),無(wú)需寫(xiě)入session了)
response.addCookie(new Cookie("token",token));
2.編寫(xiě)首頁(yè)Controller邏輯
@Controller
public class IndexController {
@Autowired
private UserMapper userMapper;
@GetMapping("/")
public String index(HttpServletRequest request){
Cookie[] cookies = request.getCookies();
if (cookies != null){
for (Cookie cookie : cookies) {
if (cookie.getName().equals("token")){
String token = cookie.getValue();
System.out.println("準(zhǔn)備進(jìn)數(shù)據(jù)庫(kù)");
User user = userMapper.findByToken(token); //去數(shù)據(jù)庫(kù)尋找該token值的用戶信息
System.out.println(user.toString());
if(user != null){ //若找到了這個(gè)用戶信息
//寫(xiě)進(jìn)session,讓頁(yè)面去展示
request.getSession().setAttribute("user",user);
}
break;
}
}
}
return "index";
}
}
3.運(yùn)行測(cè)試,成功
SpringBoot 實(shí)現(xiàn)登錄登出,登錄態(tài)管理
賬戶模塊中必要的功能登錄登出,相信這個(gè)大家都經(jīng)常使用了。簡(jiǎn)單介紹下在SpringBoot中的實(shí)現(xiàn)
先說(shuō)下實(shí)現(xiàn)思路:
用戶名密碼存儲(chǔ)在數(shù)據(jù)庫(kù)中,前端發(fā)出請(qǐng)求,攔截器先檢測(cè)用戶有無(wú)登錄,若有登錄可直接請(qǐng)求接口。無(wú)需登錄就可請(qǐng)求的接口需要加@NoLogin自定義注解。若未登錄,前端跳轉(zhuǎn)到登錄頁(yè)面,調(diào)用登錄接口,系統(tǒng)在后臺(tái)驗(yàn)證用戶名密碼,驗(yàn)證通過(guò)將用戶信息存儲(chǔ)在redis中和線程上下文中。
1.設(shè)計(jì)表結(jié)構(gòu)
除了必要的用戶名 密碼 其他賬戶信息字段大家可根據(jù)自己系統(tǒng)需求添加。
CREATE TABLE `t_account` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵', `name` varchar(64) NOT NULL DEFAULT '' COMMENT '姓名', `mobile` varchar(32) NOT NULL COMMENT '手機(jī)號(hào)', `identity` varchar(32) NOT NULL COMMENT '身份證號(hào)碼', `user_name` varchar(32) NOT NULL COMMENT '賬戶', `password` varchar(64) NOT NULL DEFAULT '' COMMENT '登錄密碼', `accept_region` bigint(20) NOT NULL COMMENT '受理中心(網(wǎng)點(diǎn))編號(hào)', `status` int(11) NOT NULL DEFAULT '1' COMMENT '狀態(tài): 0 禁用,1 正常,9 刪除', `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間', `create_by` bigint(20) DEFAULT NULL COMMENT '創(chuàng)建人Id', `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改時(shí)間', `update_by` bigint(20) DEFAULT NULL COMMENT '修改人Id', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='C端網(wǎng)點(diǎn)人員賬戶表';
2.controller層
接收客戶端傳參,調(diào)用接口與數(shù)據(jù)庫(kù)信息匹配,匹配成功返回用戶信息。并且存儲(chǔ)到redis中,且以當(dāng)前回話sessionid為key,用戶信息為value。
@RestController
@RequestMapping(value = WebConstants.WEB_PREFIX + "/account")
@Api(tags = "Account", description = "賬戶模塊")
@NoAuth
public class AccountController {
@Autowired
private AccountService accountService;
@Autowired
private StringRedisTemplate redisTemplate;
@PostMapping(value = "/login")
@ApiOperation("登錄")
public ResponseVo<AccountVo>login(@RequestBody LoginForm form, HttpServletRequest request,
HttpServletResponse response) {
HttpSession session=request.getSession();
AccountDto accountDto=accountService.login(form.getUserName(),form.getPassword());
if(null==accountDto){
throw new BizException("用戶名或密碼錯(cuò)誤!");
}
redisTemplate.opsForValue().set(session.getId(), JSON.toJSONString(accountDto));
AccountVo accountVo= BeanCopy.of(accountDto,new AccountVo()).copy(BeanUtils::copyProperties).get();
accountVo.setAceptRegion(AcceptRegionEnum.getDescByValue(accountDto.getAceptRegion()));
return ResponseVo.successResponse(accountVo);
}
@Login
@PostMapping(value = "/logout")
@ApiOperation("登出")
public ResponseVo logout(HttpServletRequest request,HttpServletResponse response){
HttpSession session=request.getSession();
session.invalidate();
redisTemplate.delete(session.getId());
return ResponseVo.successResponse();
}
}
3.創(chuàng)建請(qǐng)求攔截器
創(chuàng)建一個(gè)請(qǐng)求攔截器,用于檢測(cè)用戶登錄態(tài)。通過(guò)session_id檢測(cè)redis中有沒(méi)有用戶信息。如果存在則將用戶信息存儲(chǔ)當(dāng)前線程上下文中(用戶線程上下文實(shí)質(zhì)就是基于HashMap的緩存),便于后續(xù)使用。這一步也可以放在登錄成功后(這樣也更嚴(yán)謹(jǐn))。
@Component
public class LoginInterceptor implements HandlerInterceptor {
private Logger logger= LoggerFactory.getLogger(LoginInterceptor.class);
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Class<?> clazz = handlerMethod.getBeanType();
Method m = handlerMethod.getMethod();
//需登錄才可以訪問(wèn)的(預(yù)約核驗(yàn)?zāi)K)
if (clazz.isAnnotationPresent(NoLogin.class) || m.isAnnotationPresent(NoLogin.class)) {
return true;
}
HttpSession session=request.getSession();
//檢測(cè)redis中是否含有sessionId
String val=redisTemplate.opsForValue().get(session.getId());
if(null!=val){
logger.info(val);
AccountDto accountDto= JSON.parseObject(val,AccountDto.class);
AcceptRegionUserVistor vistor=new AcceptRegionUserVistor();
BeanUtils.copyProperties(accountDto,vistor);
AcceptRegionUserThreadContext.putSessionVisitor(vistor);
return true;
}else{
response.setStatus(401);
throw new BizException("401","common.system.user.not.login");
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
注冊(cè)攔截器:(注冊(cè)后的攔截器才會(huì)生效哦)
@Configuration
public class WebConfiguration extends WebMvcConfigurationSupport {
@Autowired
private LoginInterceptor loginInterceptor;
/**
* 攔截器配置
*
* @param registry 注冊(cè)類(lèi)
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor).addPathPatterns(WebConstants.WEB_PREFIX + "/**");
super.addInterceptors(registry);
}
}
4.登出
獲取到當(dāng)前會(huì)話,清空回話信息,刪除redis中對(duì)應(yīng)sessionid的用戶信息。代碼見(jiàn)上第二段代碼logout方法。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
使用Redis incr解決并發(fā)問(wèn)題的操作
這篇文章主要介紹了使用Redis incr解決并發(fā)問(wèn)題的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-11-11
一文帶你掌握J(rèn)ava?SPI的原理和實(shí)踐
在Java中,我們經(jīng)常會(huì)提到面向接口編程,這樣減少了模塊之間的耦合,更加靈活,Java?SPI?(Service?Provider?Interface)就提供了這樣的機(jī)制,本文就來(lái)講講它的原理與具體使用吧2023-05-05
Java中增強(qiáng)for循環(huán)在一維數(shù)組和二維數(shù)組中的使用方法
下面小編就為大家?guī)?lái)一篇Java中增強(qiáng)for循環(huán)在一維數(shù)組和二維數(shù)組中的使用方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-10-10
Java 開(kāi)發(fā)的幾個(gè)注意點(diǎn)總結(jié)
這篇文章主要介紹了Java開(kāi)發(fā)的幾個(gè)注意點(diǎn)的相關(guān)資料,需要的朋友可以參考下2016-09-09
詳解Java兩種方式簡(jiǎn)單實(shí)現(xiàn):爬取網(wǎng)頁(yè)并且保存
本篇文章主要介紹了Java兩種方式簡(jiǎn)單實(shí)現(xiàn):爬取網(wǎng)頁(yè)并且保存 ,主要用UrlConnection、HttpClient爬取實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2016-12-12
spring 自動(dòng)注入AutowiredAnnotationBeanPostProcessor源碼解析
這篇文章主要介紹了spring自動(dòng)注入AutowiredAnnotationBeanPostProcessor源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
java 數(shù)據(jù)結(jié)構(gòu)并查集詳解
并查集是一種用來(lái)管理元素分組情況的數(shù)據(jù)結(jié)構(gòu)。并查集可以高效地進(jìn)行如下操作。本文將通過(guò)Java實(shí)現(xiàn)并查集,感興趣的小伙伴可以了解一下2022-03-03

