需求:
后台管理系统的数据修改部分需要一个简易的进行登陆权限验证
原本可以的话打算用的是shrio框架进行权限管理,但是shrio框架是因为需要对HttpServletRequest进行配置相关参数,而当前使用的WebFlux并没有servlet,所以直接没法使用shrio。所以只有自己写一个基于过滤器的建议权限框架。
目录
主要有五个过滤流程:
- 判断是不是需要权限的uri存在/admin之后的页面,以及获取静态资源不进行拦截直接放行
- 判断cookie中是否存在一个token的cookie,如果没有的话则跳转到“/admin/login/“,并且在后面附带上最开始点击的页面,用于之后 登录 成功后直接进入该页面,不用重新点击。
- 如果有名为token的cookie,取出来,验证该token,是否是对应的正确的管理员用户,如果没有跳转到“/admin/login/“,附带访问页面,理由同上第二条。
- 如果存在该用户,判断该用户登录最后登陆时间是否超过了所限制的时间,如果超过,也跳转回登录页面重新登录
- 以上四个过滤全部通过的话就说明是已登录的有效用户,放行。
过滤器:
import com.miracle.qaodo.dao.ManagerUserRedisDao;
import com.miracle.qaodo.entity.ManagerUser;
import com.miracle.qaodo.util.ServerTimer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
import java.net.URI;
/**
* @Author Diuut
* @Date 2020/11/5 15:54
*/
@Configuration
@Order(0)//数字越小越优先
@Slf4j
public class AdminWebFilter implements WebFilter {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private ManagerUserRedisDao managerUserRedisDao;
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
URI uri = request.getURI();
log.info("uri:{}", uri);
if (!uri.getPath().contains("/admin") || uri.getPath().contains("/static")
|| uri.getPath().contains(".css") || uri.getPath().contains(".js")
|| uri.getPath().contains(".jpg") || uri.getPath().contains(".ico")
|| uri.getPath().contains(".png")) {
return chain.filter(exchange);
}
log.info("------AdminWebFilter-----");
HttpCookie cookie = exchange.getRequest().getCookies().getFirst("token");
String token = "";
if (cookie != null) {
token = cookie.getValue();
}
log.info("token:{}", token);
String path = uri.getPath();
String newPath = path.replace("/", "_");
if (StringUtils.isEmpty(token) && uri.getPath().contains("/admin")) {
log.info("登陆信息token为空");
ServerHttpRequest authErrorReq = request.mutate().path("/admin/login/" + newPath).build();
ServerWebExchange authErrorExchange = exchange.mutate().request(authErrorReq).build();
return chain.filter(authErrorExchange);
}
ManagerUser managerUser = managerUserRedisDao.getOneByToken(token);
log.info("managerUser:{}", managerUser);
if (managerUser == null) {
log.info("未登录,无权进行该操作");
ServerHttpRequest authErrorReq = request.mutate().path("/admin/login/" + newPath).build();
ServerWebExchange authErrorExchange = exchange.mutate().request(authErrorReq).build();
return chain.filter(authErrorExchange);
}
int nowSec = ServerTimer.distOfSecond();
int lastTime = managerUser.getLastTime();
log.info("nowSec:{} ,lastTime:{}", nowSec, lastTime);
if (nowSec - lastTime > 3600) { //超时时间秒数
log.info("登录超时重新登录");
ServerHttpRequest authErrorReq = request.mutate().path("/admin/login/" + newPath).build();
ServerWebExchange authErrorExchange = exchange.mutate().request(authErrorReq).build();
return chain.filter(authErrorExchange);
}
return chain.filter(exchange);
}
}
Controller层
@Autowired
private ManagerUserDao managerUserDao;
@RequestMapping("/loginform")
public String loginform(@RequestParam(value = "username") String username
, @RequestParam(value = "password") String password
, @RequestParam(value = "uri") String uri
, ServerWebExchange exchange, Model model) {
log.info("username:{},password:{},uri:{}", username, password, uri);
String reluri = uri.replace("_", "/");
String res = thymeleafService.managerUserLogin(username, password);
if (!res.contains("token")) {
log.info("manager==null");
model.addAttribute("info", new Dson().put("value", res)._Value());
model.addAttribute("uri", new Dson().put("value", reluri)._Value());
return "admin/login";
}
String[] tokenSplit = res.split("=");
ResponseCookie cookie = ResponseCookie.from("token", tokenSplit[1]).build();
exchange.getResponse().addCookie(cookie);
Object result = thymeleafService.indexdata();
model.addAttribute("result", result);
return "redirect:" + reluri;
}
@RequestMapping("/admin/login/{uri}")
public String login(Model model, @PathVariable("uri") String uri) {
model.addAttribute("info", new Dson().put("value", " ")._Value());
model.addAttribute("uri", new Dson().put("value", uri)._Value());
return "admin/login";
}
Service层
/**
* 管理员登录
*
* @param username 用户名
* @param password 用户密码
* @return token
*/
public String managerUserLogin(String username, String password) {
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
return "账号或密码为空";
}
ManagerUser managerUser = managerUserRedisDao.getOneByUsername(username);
if (managerUser == null) {
return "用户名或密码有误";
}
if (!StringUtils.pathEquals(managerUser.getPassword(), password)) {
return "用户名或密码有误";
}
String tokenMD5 = chessService.tokenMD5(username, password);
managerUser.setToken(tokenMD5);
managerUser.setLastTime(ServerTimer.distOfSecond());
managerUser.setLastTimeStr(ServerTimer.getFullWithS());
managerUserRedisDao.save(managerUser);
return "token=" + tokenMD5;
}
登陆页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head>
<title>Login Form</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="../../static/css/loginstyle.css" rel='stylesheet' type='text/css'/>
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
</head>
<body>
<div class="main">
<div class="login">
<h1>巧多后台数据管理</h1>
<div class="inset">
<!--start-main-->
<form name="myForm" id="myForm" action="/loginform" method="get" >
<div>
<h2>管理员登录</h2>
<h4 th:text="${info.value}" style="text-align: center;color: #e54d42"></h4>
<input type="hidden" th:value="${uri.value}" name="uri">
<span><label>用户名</label></span>
<span><input type="text" class="textbox" name="username"></span>
</div>
<div>
<span><label>密码</label></span>
<span><input type="password" class="password" name="password"></span>
</div>
<div class="sign">
<input type="submit" value="登录" class="submit" onclick="submitForm()"/>
</div>
</form>
</div>
</div>
<!--//end-main-->
</div>
</body>
</html>
各种跳转是基于现有的跳转习惯进行修改的,未登录直接进入登陆,登陆失败也会通过themleaf预留的的info值进行显示原因。