之前写过一篇关于Springboot+Shiro实现前后端分离的权限管理系统。但是由于使用了框架,对于一些小系统,实在用不上Shiro,而且还要加上学习成本。今天就来用原生的Spring技术实现登录拦截。
前后端分离
要实现前后端分离,需要考虑以下2个问题: 1. 项目不再基于session了,如何知道访问者是谁? 2. 如何确认访问者的权限?
前后端分离,一般都是通过token实现,本项目也是一样;用户登录时,生成token及 token过期时间,token与用户是一一对应关系,调用接口的时候,把token放到header或 请求参数中,服务端就知道是谁在调用接口。
源码先双手奉上:https://github.com/FENGZHIJIE1998/Auth-demo
各位客官觉得好用记得给个Star!
项目架构图

导包、配置文件都省略。
第一步编写自己的拦截器AuthInteceptor
-
-
public
class AuthInterceptor implements HandlerInterceptor {
-
@Autowired
-
private AuthService authService;
-
-
@Override
-
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
-
String token = TokenUtil.getRequestToken(request);
-
//如果token为空
-
if (StringUtils.isBlank(token)) {
-
setReturn(response,
400,
"用户未登录,请先登录");
-
return
false;
-
}
-
//1. 根据token,查询用户信息
-
UserEntity userEntity = authService.findByToken(token);
-
//2. 若用户不存在,
-
if (userEntity ==
null) {
-
setReturn(response,
400,
"用户不存在");
-
return
false;
-
}
-
//3. token失效
-
if (userEntity.getExpireTime().isBefore(LocalDateTime.now())) {
-
setReturn(response,
400,
"用户登录凭证已失效,请重新登录");
-
return
false;
-
}
-
-
return
true;
-
}
-
-
@Override
-
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
-
-
}
-
-
@Override
-
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
-
-
}
-
//返回错误信息
-
private static void setReturn(HttpServletResponse response, int status, String msg) throws IOException {
-
HttpServletResponse httpResponse = (HttpServletResponse) response;
-
httpResponse.setHeader(
"Access-Control-Allow-Credentials",
"true");
-
httpResponse.setHeader(
"Access-Control-Allow-Origin", HttpContextUtil.getOrigin());
-
//UTF-8编码
-
httpResponse.setCharacterEncoding(
"UTF-8");
-
response.setContentType(
"application/json;charset=utf-8");
-
Result build = Result.build(status, msg);
-
String json = JSON.toJSONString(build);
-
httpResponse.getWriter().print(json);
-
}
-
-
}
第二步,将拦截器配置进Spring
-
@Configuration
-
public
class InterceptorConfig implements WebMvcConfigurer {
-
-
-
@Bean
-
public AuthInterceptor authInterceptor() {
-
return
new AuthInterceptor();
-
}
-
-
@Override
-
public void addInterceptors(InterceptorRegistry registry) {
-
// 放行路径
-
List<String> patterns =
new ArrayList();
-
patterns.add(
"/webjars/**");
-
patterns.add(
"/druid/**");
-
patterns.add(
"/sys/login");
-
patterns.add(
"/swagger/**");
-
patterns.add(
"/v2/api-docs");
-
patterns.add(
"/swagger-ui.html");
-
patterns.add(
"/swagger-resources/**");
-
patterns.add(
"/login");
-
registry.addInterceptor(authInterceptor()).addPathPatterns(
"/**")
-
.excludePathPatterns(patterns);
-
}
-
}
-
第三步:测试
Controller 层
-
/**
-
* 登录校验
-
*/
-
@RestController(
"/")
-
public
class AuthController {
-
-
@Autowired
-
private AuthService authService;
-
-
/**
-
* 登录
-
*
-
* @param loginDTO
-
* @return token登录凭证
-
*/
-
@PostMapping(
"/login")
-
public Result login(@RequestBody LoginDTO loginDTO) {
-
String username = loginDTO.getUsername();
-
String password = loginDTO.getPassword();
-
//用户信息
-
UserEntity user = authService.findByUsername(username);
-
//账号不存在、密码错误
-
if (user ==
null || !user.getPassword().equals(password)) {
-
return Result.build(
400,
"用户名或密码错误");
-
}
else {
-
//生成token,并保存到数据库
-
String token = authService.createToken(user);
-
TokenVO tokenVO =
new TokenVO();
-
tokenVO.setToken(token);
-
return Result.ok(tokenVO);
-
}
-
}
-
-
/**
-
* 登出
-
*
-
* @param
-
* @return
-
*/
-
@PostMapping(
"/logout")
-
public Result logout(HttpServletRequest request) {
-
//从request中取出token
-
String token = TokenUtil.getRequestToken(request);
-
authService.logout(token);
-
return Result.ok();
-
}
-
-
/**
-
* 测试
-
*
-
* @param
-
* @return
-
*/
-
@PostMapping(
"/test")
-
public Result test( ) {
-
-
return Result.ok(
"恭喜你,验证成功啦,我可以返回数据给你");
-
}
-
-
}
-
-
编写Service层
-
@Service
-
public
class AuthServiceImpl implements AuthService {
-
-
@Autowired
-
private UserRepository userRepository;
-
-
-
@Override
-
public UserEntity findByUsername(String username) {
-
return userRepository.findByUsername(username);
-
-
}
-
-
//12小时后失效
-
private
final
static
int EXPIRE =
12;
-
-
@Override
-
public String createToken(UserEntity user) {
-
//用UUID生成token
-
String token = UUID.randomUUID().toString();
-
//当前时间
-
LocalDateTime now = LocalDateTime.now();
-
//过期时间
-
LocalDateTime expireTime = now.plusHours(EXPIRE);
-
//保存到数据库
-
user.setLoginTime(now);
-
user.setExpireTime(expireTime);
-
user.setToken(token);
-
userRepository.save(user);
-
return token;
-
}
-
-
@Override
-
public void logout(String token) {
-
UserEntity userEntity = userRepository.findByToken(token);
-
//用UUID生成token
-
token = UUID.randomUUID().toString();
-
userEntity.setToken(token);
-
userRepository.save(userEntity);
-
-
}
-
-
@Override
-
public UserEntity findByToken(String token) {
-
return userRepository.findByToken(token);
-
}
-
}
然后启动项目即可
首先数据库保存一个用户,用户管理这里就不做啦。

我们来看看效果:使用Swagger查看效果
未登录状态下:
传递错误的token:
登录成功:

传递正确的token:
token过期之后

总结:如果你看过我那篇关于Shiro前后端分离的架构,其实很容易发现,这不就是简化版的Shiro吗?拦截、验证、返回错误信息。这就是思想上的一致。Shiro底层是用Filter(过滤器)、HandlerInterceptor底层是Interceptor(拦截器)。此外,本篇不仅仅可以实现登录拦截,对于权限拦截也是妥妥的,只需要再写一个拦截器,对权限进行验证,这里就不展开了。最主要还是前后端分离的思想,由前端控制路由,后端只负责返回对应信息,这肯定是发展趋势。
有什么问题可以评论或者私信我,每日在线解(LIAO)疑(SAO)。
我是大誌,一位准备996的卑微码农🐶,觉得好用记得点赞收藏!!!
转载:https://blog.csdn.net/weixin_42236404/article/details/105252892