Java项目微服务中添加拦截器实现

目的:项目中需要拦截器的实现,让没有登陆的用户无法通过url来实现页面的渲染。

实现:

一、继承HandlerInterceptorAdapter

public class LoginInterceptor extends HandlerInterceptorAdapter

二、重写preHandler和afterCompletion

preHandler:前置方法,可以拦截在controller之前,return false被拦截,return true被放行

afterCompletion:完成方法,当视图渲染完成之后执行

/**
 * 前置方法:在handler方法执行之前执行
 * false-被拦截
 * true-放行
 * @param request
 * @param response
 * @param handler
 * @throws Exception
 */
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    // 获取token信息
    String token = CookieUtils.getCookieValue(request, this.jwtProperties.getCookieName());
    // 判断token是否为空
    if (StringUtils.isBlank(token)) {
        // 没有登录,跳转到登录页
        response.sendRedirect("登陆页面的url(从上个页面进入登陆页面的url)");
        return false;
    }
    // 解析jwt
    UserInfo userInfo = JwtUtils.getInfoFromToken(token, this.jwtProperties.getPublicKey());
    if (userInfo == null) {
        response.sendRedirect("登陆页面的url(从上个页面进入登陆页面的url)");
        return false;
    }
    THREAD_LOCAL.set(userInfo);
    return true;
}

/**
 * 完成方法:在视图渲染完成之后执行
 * @param request
 * @param response
 * @param handler
 * @param ex
 * @throws Exception
 */
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    // 必须要释放资源,使用的是tomcat线程池,业务逻辑处理完成之后,线程并没有结束,还回到线程池中了
    THREAD_LOCAL.remove();
}

三、使用ThreadLocal共享用户信息数据UserInfo

设置到ThreadLocal中在后续的业务逻辑中就可以使用UserInfo数据

private static final ThreadLocal<UserInfo> THREAD_LOCAL = new ThreadLocal<>();

THREAD_LOCAL.set(userInfo);

完整的拦截器代码:

@EnableConfigurationProperties(JwtProperties.class)
@Component
public class LoginInterceptor extends HandlerInterceptorAdapter {

    private static final ThreadLocal<UserInfo> THREAD_LOCAL = new ThreadLocal<>();

    public LoginInterceptor(JwtProperties jwtProperties) {
        this.jwtProperties = jwtProperties;
    }

    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 前置方法:在handler方法执行之前执行
     * false-被拦截
     * true-放行
     * @param request
     * @param response
     * @param handler
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取token信息
        String token = CookieUtils.getCookieValue(request, this.jwtProperties.getCookieName());
        // 判断token是否为空
        if (StringUtils.isBlank(token)) {
            // 没有登录,跳转到登录页
            response.sendRedirect("登陆页面的url(从上个页面进入登陆页面的url)");
            return false;
        }

        // 解析jwt
        UserInfo userInfo = JwtUtils.getInfoFromToken(token, this.jwtProperties.getPublicKey());
        if (userInfo == null) {
            response.sendRedirect("登陆页面的url(从上个页面进入登陆页面的url)");
            return false;
        }

        THREAD_LOCAL.set(userInfo);
        return true;
    }

    /**
     * 完成方法:在视图渲染完成之后执行
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 必须要释放资源,使用的是tomcat线程池,业务逻辑处理完成之后,线程并没有结束,还回到线程池中了
        THREAD_LOCAL.remove();
    }

    /**
     * 获取线程变量中的参数
     * @return
     */
    public static UserInfo get() {
        return THREAD_LOCAL.get();
    }
}

四、启动拦截器

两个条件:1、实现WebMvcConfigurer接口    2、添加@Configuration注解

重写addInterceptors方法:

@Override
public void addInterceptors(InterceptorRegistry registry)

五、添加拦截器和拦截路径

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(loginInterceptor()).addPathPatterns("/**");
}

其中的addPathPatterns("/**")是指拦截所有的路径

完整启动拦截器代码:

@Configuration
@EnableConfigurationProperties(JwtProperties.class)
public class ProjectConfig implements WebMvcConfigurer {

    @Autowired
    private JwtProperties jwtProperties;

    @Bean
    public LoginInterceptor loginInterceptor() {
        return new LoginInterceptor(jwtProperties);
    }

    /**
     * 注册拦截器到拦截器注册器中,使拦截器生效
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor()).addPathPatterns("/**");
    }
}

 

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,针对你的问题,我会给出一份 Java 微服务项目:家具商城的单点登录功能实现代码流程,使用 Spring Security+JWT+Redis 完成单点登录。代码实现流程如下: 1. 添加依赖 在 pom.xml 文件添加以下依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ``` 2. 配置 Spring Security 在 Spring Security 配置类添加以下配置: ``` @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserService userService; @Autowired private JwtTokenUtil jwtTokenUtil; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/auth/**").permitAll() .anyRequest().authenticated() .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring() .antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**"); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService).passwordEncoder(passwordEncoder()); } @Bean public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception { return new JwtAuthenticationTokenFilter(); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } } ``` 3. 实现 JWT 工具类 实现 JwtTokenUtil 工具类,用于生成和解析 JWT Token。 ``` @Component public class JwtTokenUtil { private static final String SECRET = "secret"; private static final String ISSUER = "issuer"; private static final String AUDIENCE = "audience"; private static final String CLAIM_KEY_USERNAME = "sub"; private static final String CLAIM_KEY_CREATED = "created"; private static final long EXPIRATION_TIME = 86400000; // 24 hours public String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername()); claims.put(CLAIM_KEY_CREATED, new Date()); return Jwts.builder() .setClaims(claims) .setIssuer(ISSUER) .setAudience(AUDIENCE) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS512, SECRET) .compact(); } public String getUsernameFromToken(String token) { String username; try { final Claims claims = getClaimsFromToken(token); username = claims.getSubject(); } catch (Exception e) { username = null; } return username; } public Date getExpirationDateFromToken(String token) { Date expiration; try { final Claims claims = getClaimsFromToken(token); expiration = claims.getExpiration(); } catch (Exception e) { expiration = null; } return expiration; } private Claims getClaimsFromToken(String token) { Claims claims; try { claims = Jwts.parser() .setSigningKey(SECRET) .parseClaimsJws(token) .getBody(); } catch (Exception e) { claims = null; } return claims; } public boolean validateToken(String token, UserDetails userDetails) { final String username = getUsernameFromToken(token); return username.equals(userDetails.getUsername()) && !isTokenExpired(token); } private boolean isTokenExpired(String token) { final Date expiration = getExpirationDateFromToken(token); return expiration.before(new Date()); } } ``` 4. 实现认证过滤器 实现 JwtAuthenticationTokenFilter 过滤器,用于在请求头解析 JWT Token,并进行认证。 ``` public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { @Autowired private UserDetailsService userDetailsService; @Autowired private JwtTokenUtil jwtTokenUtil; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { String authHeader = request.getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Bearer ")) { String authToken = authHeader.substring("Bearer ".length()); String username = jwtTokenUtil.getUsernameFromToken(authToken); if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = userDetailsService.loadUserByUsername(username); if (jwtTokenUtil.validateToken(authToken, userDetails)) { UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken); } } } chain.doFilter(request, response); } } ``` 5. 实现单点登录功能 在登录接口,使用 JWT 工具类生成 JWT Token,并将 Token 存储到 Redis ,设置 Token 过期时间,并返回 Token 给客户端。 ``` @RestController @RequestMapping("/auth") public class AuthController { @Autowired private UserService userService; @Autowired private JwtTokenUtil jwtTokenUtil; @Autowired private RedisTemplate<String, Object> redisTemplate; @PostMapping("/login") public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) { User user = userService.findByUsername(loginRequest.getUsername()); if (user == null) { return ResponseEntity.badRequest().body(new ApiResponse(false, "Invalid username")); } if (!user.getPassword().equals(loginRequest.getPassword())) { return ResponseEntity.badRequest().body(new ApiResponse(false, "Invalid password")); } UserDetails userDetails = new User(user.getUsername(), user.getPassword(), new ArrayList<>()); String token = jwtTokenUtil.generateToken(userDetails); redisTemplate.opsForValue().set(token, userDetails.getUsername(), jwtTokenUtil.EXPIRATION_TIME, TimeUnit.MILLISECONDS); return ResponseEntity.ok(new JwtAuthenticationResponse(token)); } } ``` 6. 实现单点登录拦截器 在请求过程,使用 JwtAuthenticationInterceptor 拦截器,从请求头解析 JWT Token,并从 Redis 获取 Token 对应的用户名,将用户名设置到请求属性,供后续业务使用。 ``` @Component public class JwtAuthenticationInterceptor implements HandlerInterceptor { @Autowired private JwtTokenUtil jwtTokenUtil; @Autowired private RedisTemplate<String, Object> redisTemplate; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String authHeader = request.getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Bearer ")) { String authToken = authHeader.substring("Bearer ".length()); String username = (String) redisTemplate.opsForValue().get(authToken); if (username != null && !jwtTokenUtil.isTokenExpired(authToken)) { request.setAttribute("username", username); return true; } } response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return false; } } ``` 7. 配置 Redis 在 application.properties 添加以下配置: ``` spring.redis.host=localhost spring.redis.port=6379 spring.redis.password= spring.redis.database=0 ``` 至此,Java 微服务项目:家具商城的单点登录功能使用 Spring Security+JWT+Redis 完成。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值