轉載:https://blog.csdn.net/xjj1040249553/article/details/82658889
一、pom.xml配置
-
<dependency>
-
<groupId>org.springframework.boot
</groupId>
-
<artifactId>spring-boot-starter-data-redis
</artifactId>
-
</dependency>
-
-
<dependency>
-
<groupId>org.springframework.session
</groupId>
-
<artifactId>spring-session-data-redis
</artifactId>
-
</dependency>
二、application.properties的redis配置
-
#redis
-
spring.redis.host=
127.0.
0.1
-
spring.redis.port=
6379
-
spring.redis.password=
123456
-
spring.redis.pool.max-idle=
8
-
spring.redis.pool.min-idle=
0
-
spring.redis.pool.max-active=
8
-
spring.redis.pool.max-wait=-
1
-
#超時一定要大於0
-
spring.redis.timeout=
3000
-
spring.session.store-type=redis
在配置redis時需要確保redis安裝正確,並且配置notify-keyspace-events Egx,spring.redis.timeout設置為大於0,我當時這里配置為0時springboot時啟不起來。
三、編寫登錄狀態攔截器RedisSessionInterceptor
-
//攔截登錄失效的請求
-
public
class RedisSessionInterceptor implements HandlerInterceptor
-
{
-
@Autowired
-
private StringRedisTemplate redisTemplate;
-
-
@Override
-
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
-
{
-
//無論訪問的地址是不是正確的,都進行登錄驗證,登錄成功后的訪問再進行分發,404的訪問自然會進入到錯誤控制器中
-
HttpSession session = request.getSession();
-
if (session.getAttribute(
"loginUserId") !=
null)
-
{
-
try
-
{
-
//驗證當前請求的session是否是已登錄的session
-
String loginSessionId = redisTemplate.opsForValue().get(
"loginUser:" + (
long) session.getAttribute(
"loginUserId"));
-
if (loginSessionId !=
null && loginSessionId.equals(session.getId()))
-
{
-
return
true;
-
}
-
}
-
catch (Exception e)
-
{
-
e.printStackTrace();
-
}
-
}
-
-
response401(response);
-
return
false;
-
}
-
-
private void response401(HttpServletResponse response)
-
{
-
response.setCharacterEncoding(
"UTF-8");
-
response.setContentType(
"application/json; charset=utf-8");
-
-
try
-
{
-
response.getWriter().print(JSON.toJSONString(
new ReturnData(StatusCode.NEED_LOGIN,
"",
"用戶未登錄!")));
-
}
-
catch (IOException e)
-
{
-
e.printStackTrace();
-
}
-
}
-
-
@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
-
{
-
-
}
-
}
四、配置攔截器
-
@Configuration
-
public
class WebSecurityConfig extends WebMvcConfigurerAdapter
-
{
-
@Bean
-
public RedisSessionInterceptor getSessionInterceptor()
-
{
-
return
new RedisSessionInterceptor();
-
}
-
-
@Override
-
public void addInterceptors(InterceptorRegistry registry)
-
{
-
//所有已api開頭的訪問都要進入RedisSessionInterceptor攔截器進行登錄驗證,並排除login接口(全路徑)。必須寫成鏈式,分別設置的話會創建多個攔截器。
-
//必須寫成getSessionInterceptor(),否則SessionInterceptor中的@Autowired會無效
-
registry.addInterceptor(getSessionInterceptor()).addPathPatterns(
"/api/**").excludePathPatterns(
"/api/user/login");
-
super.addInterceptors(registry);
-
}
-
}
五、登錄控制器
-
@RestController
-
@RequestMapping(value = "/api/user")
-
public
class LoginController
-
{
-
@Autowired
-
private UserService userService;
-
-
@Autowired
-
private StringRedisTemplate redisTemplate;
-
-
@RequestMapping("/login")
-
public ReturnData login(HttpServletRequest request, String account, String password)
-
{
-
User user = userService.findUserByAccountAndPassword(account, password);
-
if (user !=
null)
-
{
-
HttpSession session = request.getSession();
-
session.setAttribute(
"loginUserId", user.getUserId());
-
redisTemplate.opsForValue().
set(
"loginUser:" + user.getUserId(), session.getId());
-
-
return new ReturnData(StatusCode.REQUEST_SUCCESS, user,
"登錄成功!");
-
}
-
else
-
{
-
throw new MyException(StatusCode.ACCOUNT_OR_PASSWORD_ERROR,
"賬戶名或密碼錯誤!");
-
}
-
}
-
-
@RequestMapping(value = "/getUserInfo")
-
public ReturnData
get(long userId)
-
{
-
User user = userService.findUserByUserId(userId);
-
if (user !=
null)
-
{
-
return new ReturnData(StatusCode.REQUEST_SUCCESS, user,
"查詢成功!");
-
}
-
else
-
{
-
throw new MyException(StatusCode.USER_NOT_EXIST,
"用戶不存在!");
-
}
-
}
-
}
六、效果
我在瀏覽器上登錄,然后獲取用戶信息,再在postman上登錄相同的賬號,瀏覽器再獲取用戶信息,就會提示401錯誤了,瀏覽器需要重新登錄才能獲取得到用戶信息,同樣,postman上登錄的賬號就失效了。
瀏覽器:
postman:
七、核心原理詳解
分布式session需要解決兩個難點:1、正確配置redis讓springboot把session托管到redis服務器。2、唯一登錄。
1、redis:
redis需要能正確啟動到出現如下效果才證明redis正常配置並啟動
同時還要保證配置正確
-
@EnableCaching
-
@EnableRedisHttpSession(maxInactiveIntervalInSeconds =
30)
//session過期時間(秒)
-
@Configuration
-
public class RedisSessionConfig
-
{
-
@Bean
-
public static ConfigureRedisAction configureRedisAction()
-
{
-
//讓springSession不再執行config命令
-
return
ConfigureRedisAction
.NO_OP;
-
}
-
}
springboot啟動后能在redis上查到緩存的session才能說明整個redis+springboot配置成功!
2、唯一登錄:
1、用戶登錄時,在redis中記錄該userId對應的sessionId,並將userId保存到session中。
-
HttpSession session = request.getSession();
-
session.setAttribute("loginUserId", user.getUserId());
-
redisTemplate.opsForValue().set("loginUser:" + user.getUserId(), session.getId());
2、訪問接口時,會在RedisSessionInterceptor攔截器中的preHandle()中捕獲,然后根據該請求發起者的session中保存的userId去redis查當前已登錄的sessionId,若查到的sessionId與訪問者的sessionId相等,那么說明請求合法,放行。否則拋出401異常給全局異常捕獲器去返回給客戶端401狀態。
唯一登錄經過我的驗證后滿足需求,暫時沒有出現問題,也希望大家能看看有沒有問題,有的話給我點好的建議!