zoukankan      html  css  js  c++  java
  • Spring Boot 集成 JWT 实现单点登录授权

    使用步骤如下:
    1. 添加Gradle依赖:

    dependencies {
    implementation 'com.auth0:java-jwt:3.3.0'
    implementation('org.springframework.boot:spring-boot-starter-aop')
    }
    2. 登录检验时,使用JWT生成Token令牌(我这里登录用户名是email)。

    /**
    * 登录检验方法。
    * @param user
    * @return
    */
    public String login(User user) {
    // 登录检验逻辑 TODO

    //登录检验成功,生成token令牌
    String token = tokenService.generateToken(userRepository.findUserByMail(user.getMail()));

    //自定义返回值
    return null;
    }
    一、生成和校验Token。
    1)生成和校验token令牌的服务类。

    /**
    * 使用JWT作为Token实现。
    *
    * @author LEEMER
    * Create Date: 2019-05-20
    */
    @Service
    public class TokenService {
    private static final Logger LOGGER = LoggerFactory.getLogger(TokenService.class);

    private TokenConfigBean tokenConfigBean;

    /**
    * Token加密算法。
    */
    private Algorithm algorithm;

    /**
    * Token认证对象。
    */
    private JWTVerifier verifier;

    private UserRepository userRepository;

    public TokenService(TokenConfigBean tokenConfigBean,
    Algorithm algorithm,
    JWTVerifier verifier,
    UserRepository userRepository) {
    this.tokenConfigBean = tokenConfigBean;
    this.algorithm = algorithm;
    this.verifier = verifier;
    this.userRepository = userRepository;
    }

    public String generateToken(User user) {
    JWTCreator.Builder builder = JWT.create().withClaim("mail", user.getMail());

    builder.withClaim("expiredTime", System.currentTimeMillis()
    + tokenConfigBean.getExpiredTime() * 60 * 1000);

    return builder.sign(algorithm);
    }

    public boolean isValid(String token) {
    DecodedJWT jwt;
    try {
    jwt = verifier.verify(token);
    } catch (JWTVerificationException exception){
    LOGGER.debug("该Token解码失败:" + token);
    return false;
    }
    long expiredTime = jwt.getClaim("expiredTime").asLong();
    if (expiredTime < System.currentTimeMillis()) {
    LOGGER.debug("该Token已过期:" + expiredTime);
    return false;
    }
    return true;
    }

    public String resetExpiredTime(String token) {
    DecodedJWT jwt;
    try {
    jwt = verifier.verify(token);
    } catch (JWTVerificationException exception){
    return "";
    }
    User user = userRepository.findUserByMail(jwt.getClaim("mail").asString());
    return generateToken(user);
    }

    public String getValueByMail(String token, String key) {
    var trueToken = token.startsWith("Bearer ") ? token.substring(7) : token;
    DecodedJWT jwt;
    try {
    jwt = verifier.verify(trueToken);
    } catch (JWTVerificationException exception){
    LOGGER.debug("目标Token解析失败:" + trueToken);
    return null;
    }
    return jwt.getClaim(key).asString();
    }

    public String getValueByMail(String token, int mail) {
    return getValueByMail(token, String.valueOf(mail));
    }

    public <T> List<T> getListValueByMail(String token, String mail, Class<T> cls) {
    DecodedJWT jwt;
    try {
    jwt = verifier.verify(token);
    } catch (JWTVerificationException exception){
    return null;
    }
    return jwt.getClaim(mail).asList(cls);
    }

    public <T> List<T> getListValueByMail(String token, int mail, Class<T> cls) {
    return getListValueByMail(token, String.valueOf(mail), cls);
    }

    public User getUser(String token) {
    var mail = getValueByMail(token, "mail");
    if (StringUtils.isBlank(mail)) {
    LOGGER.error("[getUser] 从该Token中无法提取有效邮箱:{}", token);
    return null;
    }
    return userRepository.findUserByMail(mail);
    }
    }
    2)Token配置Bean:设置token过期时间、混淆。

    /**
    * @author LEEMER
    * Create Date: 2019-05-20
    */
    @Configuration
    public class TokenConfigBean {

    /**
    * Token过期时间(单位:分钟)。
    */
    private int expiredTime = 30;

    /**
    * 混淆。
    */
    private String secret = "c8e3n23ia0wgn458yqwafn934uf";

    public int getExpiredTime() {
    return expiredTime;
    }

    public void setExpiredTime(int expiredTime) {
    this.expiredTime = expiredTime;
    }

    public String getSecret() {
    return secret;
    }

    public void setSecret(String secret) {
    this.secret = secret;
    }
    }
    3)Token认证对象和加密算法配置类。

    /**
    * @author LEEMER
    * Create Date: 2019-05-20
    */
    @Component
    public class BeanConfig {

    private TokenConfigBean tokenConfigBean;

    public BeanConfig(TokenConfigBean tokenConfigBean) {
    this.tokenConfigBean = tokenConfigBean;
    }

    /**
    * Token认证对象。
    */
    @Autowired
    @Bean
    public JWTVerifier getJwtVerifier(Algorithm algorithm) {
    return JWT.require(algorithm).build();
    }

    /**
    * Token加密算法。
    */
    @Bean
    public Algorithm getAlgorithm() {
    try {
    return Algorithm.HMAC256(tokenConfigBean.getSecret());
    } catch (UnsupportedEncodingException e) {
    LOGGER.error("生成Token加法算法失败!", e);
    throw new RuntimeException(e);
    }
    }

    }
    4)自定义控制器请求token认证注解。

    /**
    * 使用在传统控制器的方法上,进行登录判断,如果没有登录则返回登录界面。
    *
    * @author LEEMER
    * Create Date: 2019-05-20
    */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ControllerAuthCheck {
    }
    /**
    * @author LEEMER
    * Create Date: 2019-05-20
    */
    @Aspect
    @Component
    public class ControllerAuthCheckAspect {

    private static final Logger LOGGER = LoggerFactory
    .getLogger(ControllerAuthCheckAspect.class);

    private UrlConfigBean urlConfigBean;

    /**
    * Token服务。
    */
    private TokenService tokenService;

    @Autowired
    public ControllerAuthCheckAspect(TokenService tokenService,
    UrlConfigBean urlConfigBean) {
    this.tokenService = tokenService;
    this.urlConfigBean = urlConfigBean;
    }

    /**
    * 定义切入点。
    *
    * 所有包含AuthCheck注解的方法均会被拦截。
    */
    @Pointcut("@annotation(cn.blackbox.annotation.ControllerAuthCheck)")
    private void checkAuth() {}

    /**
    * 环绕增强。
    *
    * 在调用相关系统模块时,进行权限检查,没有相关权限则不进行目标方法的调用。
    *
    */
    @Around(value = "checkAuth() && @annotation(controllerAuthCheck) && args(token, ..)",
    argNames = "joinPoint, controllerAuthCheck, token")
    private Object checkAuth(ProceedingJoinPoint joinPoint,
    ControllerAuthCheck controllerAuthCheck,
    String token) {
    if (!token.startsWith("Bearer ")) {
    return "redirect:" + urlConfigBean.getLogin();
    } else {
    token = token.substring(7);
    }

    if (!tokenService.isValid(token)) {
    return "redirect:" + urlConfigBean.getLogin();
    }

    try {
    return joinPoint.proceed();
    } catch (Throwable throwable) {
    LOGGER.error("控制器权限控制切面调用目标方法失败!", throwable);
    throw new RuntimeException(throwable);
    }
    }
    }
     

    二、使用Token实现相关模块登录认证。
    使用场景:请求后台数据的时候我们可以通过token值判断用户是否登录。

    1)在JS里面我们可以localStorage.getItem("token")获取token值。如下:

    function getLoginDetails() {
    $.ajax({
    url:"/api/v1/stage/land_details",
    method:"GET",
    dataType:"JSON",
    headers:{
    token:localStorage.getItem("token")
    },
    success : function (result) {

    }
    })
    }
    2)后台校验token值,通过我们自定义@ControllerAuthCheck注解实现token校验功能。

    @RequestMapping("/land_details")
    @ControllerAuthCheck // 检验token的注解,必须要获取token参数才能有效校验
    public String toLandDetailsView(@RequestParam(value = "token", required = false, defaultValue = "") String token){
    return "/land_details";
    }
    --------------------- 

  • 相关阅读:
    JBPM工作流(四)——管理流程定义
    JBPM工作流(三)——ProcessEngine与Service API
    JBPM工作流(二)——数据库表说明
    JBPM工作流(一)——实现一个简单的工作流例子
    jbpm与spring hibernate struts整合
    SpringMVC12拦截器
    SpringMVC11文件上传
    阅读代码的方法
    关于linux系统的资料
    关于图灵机的介绍(相见恨晚,太赞了)
  • 原文地址:https://www.cnblogs.com/ly570/p/11014136.html
Copyright © 2011-2022 走看看