Mon dossier de développement de cadre--2022.4.16

Préambule

Ces quelques jours de repos,

Premièrement, il y a un peu plus de choses dans l'entreprise, alors j'ai juste regardé la vidéo de Vue quand je suis rentré chez moi plus tard.

La seconde est d'étudier comment la sécurité utilise les jetons, et certaines choses sur oss (un nouveau module de fichier a été ajouté).

C'est également samedi que j'ai passé la matinée à changer la méthode basée sur la session en une méthode basée sur des jetons, encapsulant des classes d'outils redis et des classes d'outils de jeton.

Je n'ai pas fait grand-chose l'après-midi, j'ai dormi, j'ai fait une grosse bagarre et j'ai pris le coursier pour manger.

Dans le cas de Vue, vous pouvez voir 50p aujourd'hui, un total de 170P, et il faudra deux ou trois semaines pour suivre cette progression.

Quand je vois l'échafaudage vue, je devrais commencer à construire le framework frontal

XpStart–2022.4.16

Ajout d'un module de fichiers

Je prévois d'intégrer oss en premier, mais je ne sais toujours pas grand-chose sur l'accès au délai d'expiration oss.

Implémenter la sécurité + jwt

La méthode security+token est implémentée.

Ce que je pense, c'est de ne pas utiliser la méthode de configuration pour décider d'utiliser session ou token. Utilisez le jeton directement, après tout, la session a des limites

Hériter avec un filtre de jeton personnaliséBasicAuthenticationFilter

public class JwtTokenFilter extends BasicAuthenticationFilter {
    
    

    @Autowired
    private TokenUtil tokenUtil;

    @Autowired
    private RedisUtil redisUtil;

    public JwtTokenFilter(AuthenticationManager authenticationManager) {
    
    
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
    
    

        // 获取请求头的token
        String token = request.getHeader(tokenUtil.getHeader());
        // token为空,有可能是访问放行或可匿名的资源,让它继续走
        if (StringUtils.isEmpty(token)) {
    
    
            chain.doFilter(request, response);
            return;
        }
        // 验证token合法性,并取出值
        try {
    
    
            tokenUtil.verifyToken(token);
            String userName = tokenUtil.getUserName(token);
            String key = RedisKeyPrefixConstants.USER_TOKEN_PREFIX + userName;
            if (! redisUtil.hasKey(key)) {
    
    
                // token 失效,请重新登录
                ResponseData error = ResponseData.error("身份过期,请重新登录", HttpStatusConstants.UNAUTHORIZED);
                String s = new ObjectMapper().writeValueAsString(error);
                response.setCharacterEncoding("utf-8");
                response.getWriter().print(s);
                response.setStatus(org.springframework.http.HttpStatus.UNAUTHORIZED.value());
                response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            }
            else if (token.equals(redisUtil.getValue(key, new String()))){
    
    
                // todo 通过id去查询权限
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userName, null, null);
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                chain.doFilter(request, response);
            }
        } catch (Exception e) {
    
    
            // token 无效
            ResponseData error = ResponseData.error("Invalid Token", HttpStatusConstants.UNAUTHORIZED);
            String s = new ObjectMapper().writeValueAsString(error);
            response.setCharacterEncoding("utf-8");
            response.getWriter().print(s);
            response.setStatus(org.springframework.http.HttpStatus.UNAUTHORIZED.value());
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);

        }
        chain.doFilter(request, response);
    }
}

TokenUtil :

java-jwtLes dépendances que j'utilise ici peuvent être jjwtquelque peu différentes de l'API de

@Component
public class TokenUtil {
    
    

    @Value("${token.header}")
    private String header;

    @Value("${token.secret}")
    private String secret;

    @Value("${token.expire}")
    private int expire = 24 * 60;


    /**
     * 创建token
     * @param userName
     * @return
     */
    public String createToken(String userName) {
    
    
        // 添加荷载,即要保存到token的用户信息等
        JWTCreator.Builder builder = JWT.create();

        // 设置过期时间
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.MINUTE, expire);

        // 返回token
        return builder.withExpiresAt(instance.getTime()).withSubject(userName).sign(Algorithm.HMAC256(secret));
    }

    /**
     * 验证token,如果token非法,会抛出异常
     * @param token
     */
    public void verifyToken(String token) {
    
    
        JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
    }

    /**
     * 获取token中保存的信息(如果你保存了的话),token非法会抛出异常
     * @param token
     * @return
     */
    public TokenInfo getInfo(String token) {
    
    
        DecodedJWT verify = JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
        String id = verify.getClaim("id").asString();
        String userName = verify.getClaim("userName").asString();
        TokenInfo tokenInfo = new TokenInfo();
        tokenInfo.setId(id);
        tokenInfo.setUserName(userName);

        return tokenInfo;
    }

    /**
     * 验证token是否过期
     * @param token
     * @return true表示过期,false没过期
     */
    public boolean isExpire(String token) {
    
    
        try {
    
    
            Date expiresAt = JWT.decode(token).getExpiresAt();
            Date now = new Date();
            if (expiresAt.before(now)) {
    
    
                return false;
            }
        } catch (JWTDecodeException e) {
    
    
            return true;
        }
        return true;
    }

    /**
     * 获取主体,即用户名
     * @param token
     * @return
     */
    public String getUserName(String token) {
    
    
        return JWT.decode(token).getSubject();
    }

    public String getHeader() {
    
    
        return header;
    }

    public int getExpire() {
    
    
        return expire;
    }
}

Parce que la méthode de RedisUtil n'a pas été entièrement testée, elle ne sera pas publiée ici, et elle sera ajoutée plus tard lorsqu'il n'y aura pas de problème.

Je stocke le jeton généré dans redis dans le processeur où l'utilisateur se connecte avec succès

Pourquoi stocker dans redis ?

Imaginez que l'utilisateur A se connecte sur plusieurs navigateurs ou continue de se connecter sur un navigateur, puis chaque fois qu'il se connecte, le serveur crée un jeton, ce qui est déraisonnable.Par conséquent, il est stocké dans redis, qui peut contrôler un utilisateur et un jeton, et peut également être utilisé pour l'authentification unique.

Aussi, pour un utilisateur peu amical, nous le bloquons ou le supprimons du système,Le stockage du jeton dans redis peut directement invalider le jeton. Sans que les utilisateurs illégaux aient des jetons légitimes. Il en va de même dans le cas où l'utilisateur change de mot de passe.

Gestion personnalisée des exceptions d'authentification

Pour les exceptions d'authentification, nous avons juste besoin de dire "l'authentification a échoué" ou "la connexion a échoué"

@Component
public class AuthenticationException implements AuthenticationEntryPoint {
    
    
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, org.springframework.security.core.AuthenticationException authException) throws IOException, ServletException {
    
    

        ResponseData result = ResponseData.error("认证失败", HttpStatus.UNAUTHORIZED.value());
        String s = new ObjectMapper().writeValueAsString(result);
        response.setCharacterEncoding("utf-8");
        response.getWriter().print(s);
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
    }
}

Pour l'exception d'accès refusé, indiquez simplement les autorisations insuffisantes

@Component
public class AccessHandler implements AccessDeniedHandler {
    
    
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
    
    
        ResponseData result = ResponseData.error("权限不足", HttpStatus.FORBIDDEN.value());
        String s = new ObjectMapper().writeValueAsString(result);
        response.setCharacterEncoding("utf-8");
        response.getWriter().print(s);
        response.setStatus(HttpStatus.FORBIDDEN.value());
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
    }
}

Eh bien, continuez à apprendre Vue.

Enregistrement des modifications :

2022.4.22 : JwtTokenFilter
modifié. La nuit dernière, il a été constaté que le filtre de jeton précédent interceptait les ressources libérées.
Dans JwtTokenFilter, vous devez d'abord juger si le jeton est vide. S'il est vide, cela signifie que l'accès peut être des ressources libérées et que les ressources sont accessibles de manière anonyme. Si le jeton est vide, le filtre suivant est exécuté directement. La méthode doit revenir directement et se terminer sans aucun traitement.

Je suppose que tu aimes

Origine blog.csdn.net/qq_42682745/article/details/124219414
conseillé
Classement