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

/**
 * @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過期)便會重新向到登錄頁面,重新登陸。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM