Mi registro de desarrollo del marco: 2022.4.16

Preámbulo

Estos pocos días libres,

Primero, hay un poco más de cosas en la empresa, así que vi el video de Vue cuando llegué a casa más tarde.

El segundo es estudiar cómo la seguridad usa tokens y algunas cosas sobre oss (se ha agregado un nuevo módulo de archivo).

También fue el sábado que pasé la mañana cambiando el método basado en sesión a basado en token, encapsulando clases de herramientas de redis y clases de herramientas de token.

No hice mucho en la tarde, dormí, jugué una gran pelea y tomé el correo para comer.

En el caso de Vue, hoy se pueden ver 50p, un total de 170P, y se necesitarán dos o tres semanas para seguir este progreso.

Cuando veo el andamiaje de vue, debería comenzar a construir el marco de front-end

XpStart–2022.4.16

Se agregó un módulo de archivo

Planeo integrar oss primero, pero todavía no sé mucho sobre el acceso al tiempo de caducidad de oss.

Implementar seguridad + jwt

Se implementa el método de seguridad+token.

Lo que pienso es no usar el método de configuración para decidir si usar sesión o token. Use el token directamente, después de todo, la sesión tiene limitaciones

Heredar con filtro de token personalizadoBasicAuthenticationFilter

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-jwtLas dependencias que uso aquí pueden ser jjwtalgo diferentes de la 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;
    }
}

Debido a que el método de RedisUtil no se ha probado por completo, no se publicará aquí y se agregará más adelante cuando no haya ningún problema.

Almaceno el token generado en redis en el procesador donde el usuario inicia sesión correctamente

¿Por qué almacenar en redis?

Imagine que el usuario A inicia sesión en varios navegadores o sigue iniciando sesión en un navegador, luego, cada vez que inicia sesión, el servidor creará un token, lo cual no es razonable.Por lo tanto, se almacena en redis, que puede controlar un usuario y un token, y también se puede usar para el inicio de sesión único.

Además, para un usuario hostil, lo bloqueamos o eliminamos del sistema,Almacenar el token en redis puede invalidarlo directamente. Sin causar que los usuarios ilegales tengan tokens legítimos. Lo mismo se aplica en el caso de que el usuario cambie la contraseña.

Gestión de excepciones de autenticación personalizada

Para las excepciones de autenticación, solo necesitamos decir "autenticación fallida" o "inicio de sesión fallido"

@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);
    }
}

Para la excepción de acceso denegado, solo indique permisos insuficientes

@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);
    }
}

Bueno, sigue aprendiendo Vue.

Registro de modificación:

2022.4.22: JwtTokenFilter
modificado. Anoche, se descubrió que el filtro de token anterior interceptaría los recursos liberados.
En JwtTokenFilter, primero debe juzgar si el token está vacío. Si está vacío, significa que el acceso puede liberar recursos y se puede acceder a los recursos de forma anónima. Si el token está vacío, el siguiente filtro se ejecuta directamente. El método debe regresar directamente y finalizar sin ningún procesamiento.

Supongo que te gusta

Origin blog.csdn.net/qq_42682745/article/details/124219414
Recomendado
Clasificación