通过JWT验证登录

 config层中的 MVCconfig

@Configuration
@ComponentScan(basePackages = "edu.nf.demo")
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {

    /**
     * 使用默认servlet处理静态资源
     * @param configurer
     */
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    /**
     * 全局跨域设置
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedHeaders("*")
                .allowedMethods("*");
    }

    /**
     * 装配认证拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuthTokenInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/login");
    }
}

WebInitializer 
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[0];
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{MvcConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

定义一个JWT的工具类


@Slf4j
public class JwtUtils {

    /**
     * 秘钥,用于signature(签名)部分的加密和解密
     */
    private static final String KEY = "nf-it";
    /**
     * 签发机构
     */
    private static final String ISS = "nf";

    /**
     * 创建token
     * @param claims 数据载体
     * @param ttl token的过期时间
     * @return
     */
    public static String createToken(Map<String, Object> claims, long ttl) {
        JwtBuilder builder = Jwts.builder()
                //获取签名秘钥,并采用HS256的加密算法进行提签名
                .signWith(SignatureAlgorithm.HS256, KEY)
                //jwt唯一标识
                .setId(UUID.randomUUID().toString())
                //设置数据内容
                .setClaims(claims)
                //设置签发人
                .setIssuer(ISS)
                //主题
                .setSubject("JWT AUTH")
                //签发时间
                .setIssuedAt(new Date());
        //设置过期时间
        if(ttl > 0) {
            builder.setExpiration(getExpDate(ttl));
        }
        //创建jwt字符串并返回
        return builder.compact();
    }

    /**
     * 获取载体的数据
     * @param token jwt
     * @param name json载体数据中的name
     * @param type 载体数据的类型
     * @return
     * @param <T>
     */
    public static <T> T getPayload(String token, String name, Class<T> type) {
        return Jwts.parser()
                //解密
                .setSigningKey(KEY)
                //解析token中的数据载体部分
                .parseClaimsJws(token)
                //取出载体数据
                .getBody()
                //根据name和type取中相应的value
                .get(name, type);
    }

    /**
     * 验证token的有效性,如果验证不通过会抛出相应的异常
     * @param token
     */
    public static void verify(String token) {
        Jwts.parser().setSigningKey(KEY).parseClaimsJws(token);
    }

    /**
     * 根据用户传的ttl计算出过期时间
     * @param ttl
     * @return
     */
    private static Date getExpDate(long ttl) {
        //当前时间 + ttl = 过期时间
        Date expDate = new Date(System.currentTimeMillis() + ttl);
        log.debug("Token过期时间:" + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(expDate));
        return expDate;
    }
}

局部异常处理

public class GlobalException extends RuntimeException {

    private Integer errorCode;

    public GlobalException(Integer errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }

    public Integer getErrorCode() {
        return errorCode;
    }
}

public class LoginException extends GlobalException {

    public LoginException(Integer errorCode, String message) {
        super(errorCode, message);
    }
}

token拦截器

/**
 * @author wangl
 * @date 2022/10/26
 * token验证拦截器
 */
public class AuthTokenInterceptor implements HandlerInterceptor {

    /**
     * 请求头的name
     */
    private static final String HEADER = "Authorization";
    /**
     * token前缀,注意有个空格
     */
    private static final String TOKEN_PREFIX = "Bearer ";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //解析token
        String token = request.getHeader(HEADER);
        if(token != null) {
            //得到jwt
            String jwt = token.replace(TOKEN_PREFIX, "");
            //验证token,验证未通过会引发相关异常
            try {
                JwtUtils.verify(jwt);
                //获取载体数据
                Integer uid = JwtUtils.getPayload(jwt, "uid", Integer.class);
                //将其保存到请求作用域,在controller中就可以通过@RequestAttribute注解获取到
                request.setAttribute("uid", uid);
                //放行
                return true;
            } catch (SecurityException e) {
                responseView(response, "无效的签名");
            } catch (MalformedJwtException e) {
                responseView(response, "无效的token");
            } catch(ExpiredJwtException e) {
                responseView(response, "Token已过期");
            } catch(UnsupportedJwtException e) {
                responseView(response, "不支持的Token类型");
            }
            return false;
        }
        //没有token表示未登录
        responseView(response, "请先登陆");
        return false;
    }

    /**
     * 统一响应视图
     * @param response
     * @param message
     */
    private void responseView(HttpServletResponse response, String message) throws IOException {
        ResultVO vo = new ResultVO();
        vo.setCode(HttpStatus.UNAUTHORIZED.value());
        vo.setMessage(message);
        response.setContentType("application/json;charset=utf-8");
        String json = new ObjectMapper().writeValueAsString(vo);
        response.getWriter().println(json);
    }
}

service接口

public interface LoginService {

    /**
     * 登录认证,认证通过后颁发token
     * @param account
     * @param password
     * @return
     */
    String auth(String account, String password);
}

service中的impl

@Service
public class LoginServiceImpl implements LoginService {

    @Override
    public String auth(String account, String password) {
        if("user1".equals(account) && "123".equals(password)) {
            //封装数据载体
            Map<String, Object> map = new HashMap<>();
            map.put("uid", 1001);
            //生成jwt
            String jwt = JwtUtils.createToken(map, 120000L);
            return jwt;
        }
        throw new LoginException(10000, "账号或密码错误");
    }
}

vo层

@Data
public class ResultVO<T> {
    private Integer code;
    private String message;
    private T data;
}

controller中的BaseController 

public class BaseController {

    public <T> ResultVO<T> success(T data) {
        ResultVO<T> vo = new ResultVO<>();
        vo.setCode(HttpStatus.OK.value());
        vo.setData(data);
        return vo;
    }

    public ResultVO success() {
        ResultVO vo = new ResultVO();
        vo.setCode(HttpStatus.OK.value());
        return vo;
    }
}

LoginController

@RestController
@RequiredArgsConstructor
public class LoginController extends BaseController {

    private final LoginService service;

    @PostMapping("/login")
    public ResultVO login(String account, String password,
                          HttpServletResponse response) {
        String jwt = service.auth(account,password);
        //将jwt添加到响应头带回客户端
        response.addHeader("token", jwt);
        return success();
    }
}

UserController

当我们的没有登录时,就不能读取到这个controller的方法,也不能读取这个页面

@RestController
@Slf4j
public class UserController extends BaseController{

    /**
     * 如果用户未登录是不能请求这个url
     * @param id 从请求作用域获取的id
     * @return
     */
    @GetMapping("/user")
    public ResultVO<String> getUser(@RequestAttribute("uid") int id) {
        log.debug("根据id查询用户讯息");
        //测试数据
        String data = "{" + id + ": user1}";
        return success(data);
    }
}

当我们输出错误时,就会报这个错 

正确时

由于我们没有添加跳转接收,就算输出正确也会显示出错误,这个不用担心

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值