springboot项目使用jwt+token实现登录

首先我们为什么要用token呢?

session和token都是用来保持会话,功能相同;

session用于临时保存用户信息,session存储在服务端,生命周期跟随服务器状态而变化。
token比session更安全。token不存在于服务端,可跨域调用接口信息。token的信息是加密且有实效性的,每次请求过来都需要重新验证

接下来我们直接展示实现代码:

pom文件需要先引入jwt依赖:

        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.3.0</version>
        </dependency>

然后是加密的用户信息实体类:

import com.alibaba.fastjson.annotation.JSONField;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;

/**
 * (TahLoginUser)实体类
 *
 * @author makejava
 * @since 2021-12-14 15:09:14
 */
@Data
public class TahLoginUser implements Serializable {

    private static final long serialVersionUID = 833285830459008034L;
    
    private Integer id;

    /**
    * 登录名(不能重复)
    */
    @ApiModelProperty(value = "登录名(不能重复)",required = true)
    private String userName;
    /**
    * 密码
    */
    @ApiModelProperty(value = "密码",required = true)
    private String password;
    /**
    * 钉钉id
    */
    @ApiModelProperty(value = "钉钉id",required = true)
    private String userId;
    /**
    * 真实姓名
    */
    @ApiModelProperty("真实姓名")
    private String realName;
    /**
    * 电话
    */
    @ApiModelProperty("电话")
    private String mobile;
    /**
    * 邮箱
    */
    @ApiModelProperty("邮箱")
    private String email;

}

接下来是jwt加密工具类:

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.google.gson.Gson;
import com.haileer.dd.dingdingserver.entity.TahLoginUser;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;

public class JwtUtil {

    final static String SECRET = "jwtToken";//私钥
    final static Gson gson = new Gson();
    final static long TOKEN_EXP = 60 * 60 * 24 * 30;//过期时间 三十天
//    final static long TOKEN_EXP = 240 ;//过期时间,测试使用

    public static String createJwt(TahLoginUser tahLoginUser) throws UnsupportedEncodingException {
        Algorithm al = Algorithm.HMAC256(SECRET);
        Instant instant = LocalDateTime.now().plusSeconds(TOKEN_EXP).atZone(ZoneId.systemDefault()).toInstant();
        Date expire = Date.from(instant);
        Gson gson = new Gson();
        String s = gson.toJson(tahLoginUser);
        String token = JWT.create()
                .withSubject("userInfo")
                .withClaim("user", s)
                .withExpiresAt(expire)
                .sign(al);
        return token;
    }

    /**
     * @Param: 传入token
     * @return:
     */
    public static boolean verify(String token) throws UnsupportedEncodingException {
        try {
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT jwt = verifier.verify(token);
            if (jwt.getExpiresAt().before(new Date())) {
                System.out.println("token已过期11");
                return false;
            }
        } catch (Exception e) {
            System.out.println("token已过期22");
            return false;
        }
        return true;
    }

    /**
     * 获取用户信息
     *
     * @param request
     * @return
     */
    public static TahLoginUser getUserIdByToken(HttpServletRequest request) throws UnsupportedEncodingException {
        String header = request.getHeader("Authorization");
        String token = header.substring(7);
        Algorithm algorithm = Algorithm.HMAC256(SECRET);
        JWTVerifier verifier = JWT.require(algorithm).build();
        DecodedJWT jwt = verifier.verify(token);
        Claim claim = jwt.getClaim("user");
        String json = claim.asString();
        TahLoginUser tbLoginUser = gson.fromJson(json, TahLoginUser.class);
        return tbLoginUser;
    }


}

封装用户信息工具类:


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * <p>
 * Title: SecurityUserHolder.java
 * </p>
 *
 * <p>
 * Description: SpringSecurity用户获取工具类,该类的静态方法可以直接获取已经登录的用户信息
 * </p>
 *
 * @author honghu
 * @version honghu_b2b2c 8.0
 * @date 2014-4-24
 */
public class SecurityUserHolder {

    @SuppressWarnings("unused")
    private static Logger logger = LoggerFactory.getLogger(SecurityUserHolder.class);

    public static String prefix = "user_token";

    //新增头部参数
    private static String outfallTypePrefix = "outfall_type";

    public static Integer getCurrentUserId() {
        Integer userId = null;
        if (RequestContextHolder.getRequestAttributes() != null) {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            try {
                userId = (Integer) request.getSession().getAttribute(prefix);
            } catch (Exception e) {
                request.getSession().removeAttribute(prefix);
                return null;
            }
        }
        return userId;
    }

    public static Integer getOutfallType() {
        Integer outfallType = null;
        if (RequestContextHolder.getRequestAttributes() != null) {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            try {
                outfallType = (Integer) request.getSession().getAttribute(outfallTypePrefix);
            } catch (Exception e) {
                request.getSession().removeAttribute(outfallTypePrefix);
                return null;
            }
        }
        return outfallType;
    }

    public static void setCurrentUserId(Integer userId) {
        if (RequestContextHolder.getRequestAttributes() != null) {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            request.getSession().setAttribute(prefix, userId);
        }
    }

    public static void setOutfallType(Integer outfallType) {
        if (RequestContextHolder.getRequestAttributes() != null) {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            request.getSession().setAttribute(outfallTypePrefix, outfallType);
        }
    }

    public static void removeCurrentUser() {
        if (RequestContextHolder.getRequestAttributes() != null) {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            request.getSession().setAttribute(prefix, null);
            request.getSession().removeAttribute(prefix);
        }
    }

    public static void removeOutfallType() {
        if (RequestContextHolder.getRequestAttributes() != null) {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            request.getSession().setAttribute(outfallTypePrefix, null);
            request.getSession().removeAttribute(outfallTypePrefix);
        }
    }


}

配置拦截器以及token解析拦截类:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 拦截器链配置
 *
 * @author wsj
 * @date 2019/08/13
 */
@Configuration
public class InteceptorChainConfig implements WebMvcConfigurer {

    @Bean
    public HandlerInterceptor getMyInterceptor() {
        /**
         * bean注解提前加载  解决@Autowired为空的情况
         */
        return new AuthenticationInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //请求参数转换  过滤登录导出图片路径
        registry.addInterceptor(getMyInterceptor()).addPathPatterns("/**").excludePathPatterns(
                "/login/login",//登录接口不拦截 ,其他不需要拦截的接口也在此配置
                "/login/webLogin",
                "/login-user/web-login",
                "/swagger-resources/**",//swagger相关请求路径也不需要拦截
                "/webjars/**",
                "/v2/**",
                "/swagger-ui.html/**",
                "/doc.html", "/doc.html/**");
    }


    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Configuration
public class AuthenticationInterceptor implements HandlerInterceptor {

    @Autowired
    ApplicationArguments applicationArguments;

    @Autowired
    TahLoginUserService loginUserService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if ("OPTIONS".equals(request.getMethod())) {//这里通过判断请求的方法,判断此次是否是预检请求,如果是,立即返回一个204状态吗,标示,允许跨域;预检后,正式请求,这个方法参数就是我们设置的post了
            response.setStatus(HttpStatus.NO_CONTENT.value()); //HttpStatus.SC_NO_CONTENT = 204
            response.setHeader("Access-Control-Allow-Methods", "POST, GET, DELETE, OPTIONS, DELETE");//当判定为预检请求后,设定允许请求的方法
            response.setHeader("Access-Control-Allow-Headers", "Content-Type, x-requested-with, Token"); //当判定为预检请求后,设定允许请求的头部类型
            response.addHeader("Access-Control-Max-Age", "1");
            return true;
        }

        //过滤静态资源图片
        String servletUrl = request.getServletPath();
        if (servletUrl != null && ("/favicon.ico".equals(servletUrl) || "/error".equals(servletUrl))) {
            return true;
        }

        ResultDto<String> resultDto = new ResultDto<>();
        // 从 http 请求头中取出 token
        String header = request.getHeader("Authorization");
        if (StringUtils.isEmpty(header)) {
            SecurityUserHolder.removeCurrentUser();
            resultDto.setError(401, "用户未登录");
            System.out.println("未登录url="+servletUrl);
            throw new CustomException(resultDto);
        }

        String userId = "";
        if (!StringUtils.isEmpty(header)) {
            if (!header.startsWith("Bearer")) {
                SecurityUserHolder.removeCurrentUser();
                resultDto.setError(401, "token错误");
                throw new CustomException(resultDto);
            }
            String token = header.substring(7);
            if (null == header || header.trim().equals("")) {
                SecurityUserHolder.removeCurrentUser();
                resultDto.setError(401, "请求头中没有token");
                throw new CustomException(resultDto);
            }
            boolean verity = JwtUtil.verify(token);
            if (!verity) {
                SecurityUserHolder.removeCurrentUser();
                resultDto.setError(401, "token已过期");
                throw new CustomException(resultDto);
            }
            //拿到userid
            userId = JwtUtil.getUserIdByToken(request).getUserId();
        }

        TahLoginUser user = loginUserService.queryByUserId(userId);
        if (user == null) {
            SecurityUserHolder.removeCurrentUser();
            resultDto.setError(401, "用户不存在");
            throw new CustomException(resultDto);
        }
        //存入userId
        SecurityUserHolder.setCurrentUserId(userId);
        return true;
    }


    @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 {}

}

所用到的自定义CustomException文件:

/**
 * 自定义异常
 *
 * @author ruoyi
 */
public class CustomException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    private Integer code;

    private String message;

    public CustomException(ResultDto<String> resultDto) {
        this.message = resultDto.getMessage();
        this.code = resultDto.getCode();
    }

    @Override
    public String getMessage() {
        return message;
    }

    public Integer getCode() {
        return code;
    }
    
}

接下来展示登录接口:


    @PostMapping("/web-login")
    @ApiOperation("web端登录")
    public String weblogin1(@RequestParam String username, @RequestParam String password) {
        try {
            if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
                return "用户名或密码不能为空";
            }
            TahLoginUser tbLoginUser = new TahLoginUser();
            tbLoginUser.setUserName(username);
            tbLoginUser.setPassword(password);
            List<TahLoginUser> list = loginUserService.queryUserInfo(tbLoginUser);
            if (list == null || list.isEmpty()) {
                return "该用戶不存在";
            }
            if (!list.get(0).getPassword().equals(password)) {
                return "密码不正确";
            }
            TahLoginUser user = list.get(0);
            //加密用户信息
            String token = JwtUtil.createJwt(user);
            return token;
        } catch (Exception e) {
            return e.getMessage();
        }
    }

从登录接口获取的token,存储在客户端, 客户端下次发起请求时放在header中Authorization中,

AuthenticationInterceptor拦截器直接解析,解析通过后放行,否则抛出异常。

到此token登录代码部分就全部完成了,下次见!

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
单点登录(Single Sign-On,简称SSO)是一种身份验证技术,可以让用户只需一次登录,就可以访问多个应用程序。在实际开发中,我们可以使用Spring Boot、JWT和Redis来实现单点登录功能。 下面是实现单点登录的步骤: 1. 创建Spring Boot项目并引入所需依赖:spring-boot-starter-web、spring-boot-starter-data-redis和jjwt。 2. 创建一个User实体类,包含用户名和密码等信息。 3. 创建一个UserService,实现对用户信息的操作,包括注册、登录等。 4. 引入JWT依赖后,我们需要创建一个JWTUtil类,实现token的生成和解析。 5. 创建一个LoginController,用于处理用户的登录请求。在登录成功后,生成token并将其存储到Redis中。 6. 创建一个AuthController,用于验证用户的token是否有效。在验证成功后,可以获取用户信息并返回。 7. 在需要进行单点登录验证的应用程序中,只需要在请求中携带token,并调用AuthController进行验证即可。 具体实现细节可以参考以下代码示例: User实体类: ```java public class User { private String username; private String password; // 省略setter和getter方法 } ``` UserService接口: ```java public interface UserService { void register(User user); String login(String username, String password); } ``` UserService实现类: ```java @Service public class UserServiceImpl implements UserService { @Autowired private RedisTemplate<String, String> redisTemplate; @Override public void register(User user) { // 省略用户注册逻辑 } @Override public String login(String username, String password) { // 省略用户登录逻辑 // 登录成功后生成token并存储到Redis中 String token = JWTUtil.generateToken(username); redisTemplate.opsForValue().set(username, token, 30, TimeUnit.MINUTES); return token; } } ``` JWTUtil类: ```java public class JWTUtil { private static final String SECRET_KEY = "my_secret_key"; private static final long EXPIRATION_TIME = 3600000; public static String generateToken(String username) { return Jwts.builder() .setSubject(username) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); } public static String getUsernameFromToken(String token) { return Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody() .getSubject(); } } ``` LoginController: ```java @RestController public class LoginController { @Autowired private UserService userService; @PostMapping("/login") public ResponseEntity<String> login(@RequestBody User user) { String token = userService.login(user.getUsername(), user.getPassword()); return ResponseEntity.ok(token); } } ``` AuthController: ```java @RestController public class AuthController { @Autowired private RedisTemplate<String, String> redisTemplate; @GetMapping("/auth") public ResponseEntity<User> auth(@RequestHeader("Authorization") String token) { String username = JWTUtil.getUsernameFromToken(token); if (StringUtils.isEmpty(username)) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } String redisToken = redisTemplate.opsForValue().get(username); if (!token.equals(redisToken)) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } User user = new User(); user.setUsername(username); return ResponseEntity.ok(user); } } ``` 在请求中携带token的示例: ```java @Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplate(); restTemplate.setInterceptors(Collections.singletonList((request, body, execution) -> { String token = // 从Redis中获取token request.getHeaders().add("Authorization", token); return execution.execute(request, body); })); return restTemplate; } } ``` 以上就是使用Spring Boot、JWT和Redis实现单点登录的步骤和示例代码。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值