Spring Boot实现简单的Token登录验证

Session登录

登录流程

浏览器第一次向服务器发出请求,服务器接收到请求后会检测这个请求中是否包含一个叫做JSESSIONID的cookie,如果包含,就在自己的JVM缓存中查找此cookie是否对应一个session对象,如果没有则为其创建一个,用来维持对话。如果请求中没有叫做JSESSIONID的cookie,那么就说明该用户是第一次发出请求,那么服务器会为其生成一个名为JSESSIONID的cookie以及对应的session对象,并且服务器会将这个名为JSESSIONID的cookie返回给浏览器。这样浏览器再次发出请求时就会自动带上这个cookie,以便维持本次会话。

弊端

传统的Session登录需要服务器位置session对象,从而维持对话状态。这样做当然可以,当时当用户量激增的时候,每个用户在线用户都要对应一个session对象,那么势必会对服务器的性能造成影响。

Token登录

Token登录是目前市面上比较常用的登录验证方式。与一般的Session不同,用户在登录的时候,如果此用户的账号、密码都正确,那么后台会根据用户信息(用户名、密码等等)为该用户生成一个加密字符串。对,Token本质上就是一个记录了用户信息的加密字符串。加密是处于安全的考虑,防止Token字符串被破解及篡改。

那么这个Token字符串该如何返回给用户呢?我知道的有两种方式,一种是将其放到cookie中,这样这要这个cooke不过其,那么浏览器每次发出请求时都会携带这个token字符串。服务器便能根据这个字符串获取用户信息。从而进行一系列的操作,如登录验证、权限管理等。

实现

这里仅仅是简单的对其实现,采用jwt的方案。

1.添加依赖

 <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>

2.编写Token工具类

@Component
public class TokenUtil{

    /**
     * 生成token
     * @param user
     * @return
     */
    public String generateToken(User user) {
        Date start = new Date();
        long currentTime = System.currentTimeMillis() + 60* 60 * 1000;//一小时有效时间
        Date end = new Date(currentTime);
        String token = "";
        token = JWT.create()
                .withAudience(user.getId().toString())
                .withAudience(user.getUsername())
                .withIssuedAt(start)
                .withExpiresAt(end)
                .sign(Algorithm.HMAC256(user.getPassword()));
        return token;
    }


    /**
     *
     * @param token
     * @param key
     * @return userId
     * 获取制定token中某个属性值
     */
    public static String get(String token, String key) {
        List<String> list= JWT.decode(token).getAudience();
        String userId = JWT.decode(token).getAudience().get(0);
        return userId;
    }

    /**
     * 获取request
     * @return
     */
    public static HttpServletRequest getRequest() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        return requestAttributes == null ? null : requestAttributes.getRequest();
    }


    /**
     *
     * @param request
     * @return
     * 获取token
     */
    public String getToken(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        for (Cookie c :
                cookies) {
            if (c.getName().equals("token")) {
                return c.getValue();
            }
        }
        return null;
    }
}

3.自定义拦截器类

实现HandlerInterceptor接口,并实现preHandle方法。preHandle方法会在每一个Controller方法执行前执行,因此我们可以在该方法中获取token,从而进行一系列操作。在这里对其进行简单的实现,通过request对象获取名为token的cookie,如果存在则放行,如果不存在则拦截,并重定向到登录界面。

public class AuthenticationInterceptor implements HandlerInterceptor {
    @Autowired
    private TokenUtil tokenUtil;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
//        查看请求中是否存在token,如果不存在直接跳转到登陆页面
        String token = tokenUtil.getToken(request);
        if (StringUtils.isEmpty(token)) {
            response.sendRedirect("/login");
            return false;
        }
        return true;
    }
}

4.实现拦截

@Configuration
public class MVCConfig implements WebMvcConfigurer {
    /**
     * 静态资源映射
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticationInterceptor())
//                表示拦截所有请求
                .addPathPatterns("/**")
//                表示取消对特定路径的拦截
                .excludePathPatterns("/login")
                .excludePathPatterns("/loginCheck")
//                这里一定不要写成/**/*.js的形式,spring boot无法识别
//                取消对static目录下静态资源的拦截
                .excludePathPatterns("/static/**");
    }

    @Bean
    public AuthenticationInterceptor authenticationInterceptor() {
        return new AuthenticationInterceptor();
    }
}

5.业务代码

Controller

@Controller
public class UserController {

    @Autowired
    private IUserService userService;


    @GetMapping("/login")
    public String toLoginPage(){
        return "login";
    }

    @PostMapping("/loginCheck")
    @ResponseBody
    public R login(@RequestBody User user, HttpServletResponse response){
        R result = userService.loginCheck(user, response);
        return result;
    }


Service

package com.yuqiang.shop.service.impl;

import com.yuqiang.shop.entity.Page;
import com.yuqiang.shop.entity.User;
import com.yuqiang.shop.mapper.UserMapper;
import com.yuqiang.shop.service.IUserService;
import com.yuqiang.shop.utill.R;
import com.yuqiang.shop.utill.TokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * @author: OnlyOne
 * @create: 2020-12-22 14:42
 * @description:
 **/
@Service
public class UserServiceImpl implements IUserService {

    @Autowired
    private UserMapper userMapper;


    @Autowired
    TokenUtil tokenUtil;

    @Override
    public R loginCheck(User user, HttpServletResponse response) {
        User user2 = userMapper.selectByName(user.getUsername());
        if (user2 == null) {
            return R.error().message("该用户不存在!");
        }
        if (!user2.getPassword().equals(user.getPassword())) {
            return R.error().message("密码错误!");
        }
        String token = tokenUtil.generateToken(user2);
        Cookie cookie = new Cookie("token", token);
//        设置cookie的作用域:为”/“时,以在webapp文件夹下的所有应用共享cookie
        cookie.setPath("/");
        response.addCookie(cookie);
        return R.ok().message("登录成功!");
    }
}

这样当用户成功登录时,便会获得一个名为token的cookie,以后用户所有的请求都会带上这个token。如果请求中没有token(未登录或者token对应的cookie过期)便会重新向到登录页面,重新登陆。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值