Java实现基于token认证的方法示例

我来为您讲解“Java实现基于token认证的方法示例”的完整攻略。

什么是token认证

Token认证是现在比较流行的Web应用程序认证方法之一。它能解决基于session认证的一些问题,比如跨站点请求伪造(CSRF)和分布式系统中的会话共享的问题。用户只需要通过用户名和密码一次验证,在服务器成功认证后,服务器会返回一个token给客户端。客户端在后续的访问请求中带上这个token来进行身份验证,避免了使用 cookie 造成的一些基于 session 认证的安全问题。

实现基于token认证的方法

要实现基于token认证的方法具体分为以下步骤:

  1. 用户登录时,客户端向服务器发送用户名和密码。
  2. 服务器验证用户名和密码是否正确,如果正确则在服务器端生成一个token,并保存该 token 到数据库中。同时返回给客户端该token作为登录凭证。
  3. 客户端将这个 token 存储在本地存储器中,例如localStorage。
  4. 在每次向服务器发送请求时,客户端将token添加到请求的头部中,例如Authorization头部。
  5. 服务器通过获取请求头部中的 Authorization 字段获取该请求的token,然后从数据库中查询该 token 是否存在。如果存在,则说明该请求的用户已经通过了身份认证,服务器为请求提供相应的服务。

下面给出一个Java实现基于token认证的示例,帮助大家更好的理解token认证的流程。

示例一

这个示例使用Spring框架实现基于token认证的方法。

首先,我们需要在Spring Security中配置Token-Based Authentication:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private JwtAuthenticationEntryPoint unauthorizedHandler;

    @Autowired
    pivate JwtTokenUtil jwtTokenUtil;

    @Bean
    public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
        return new JwtAuthenticationTokenFilter();
    }

    @Override
    protected voird configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.csrf().disable()
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .antMatchers("/auth/**").permitAll()
                .anyRequest().authenticated();

        // 添加一个过滤器以验证token
        httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);

        // 禁用缓存
        httpSecurity.headers().cacheControl();
    }

    @Override
    public void configure(WebSecurity webSecurity) throws Exception {
        // 允许 Swagger2 访问
        webSecurity.ignoring().antMatchers("/v2/api-docs", "/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/images/**");
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

在上述代码中,我们从用户获取其username和password来认证,如果成功,将生成并返回一个JWT token。此时,token已经保存在客户端中,可以在以后的请求中使用。

接下来,我们需要实现一个类来提供token的生成和验证功能。下面是一个示例:

@Service
public class JwtTokenService {

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

    // 定义过期时间为1小时
    private static final long EXPIRATION_TIME = 60 * 60 * 1000;

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("sub", userDetails.getUsername());
        claims.put("iat", new Date(System.currentTimeMillis()));
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    public boolean validateToken(String token, UserDetails userDetails) {
        final String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }

    public String getUsernameFromToken(String token) {
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getSubject();
    }

    private Date getExpirationDateFromToken(String token) {
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getExpiration();
    }

    private boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }
}

在上面的代码中,我们定义了如下方法:

  • generateToken:生成token;
  • validateToken:验证token是否有效;
  • getUsernameFromToken:获取token中的用户名;
  • getExpirationDateFromToken:获取token的过期时间;
  • isTokenExpired:判断token是否过期。

我们还可以验证前端传过来的 token 是否合法,下面是一个示例:

@Service
public class JwtUserDetailsService implements UserDetailsService {

    @Autowired
    private JwtTokenService jwtTokenService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 假设这里是从数据库中查询用户信息
        return new User(username, "", new ArrayList<>());
    }

    public boolean validateToken(String token) {
        String username = jwtTokenService.getUsernameFromToken(token);
        UserDetails userDetails = this.loadUserByUsername(username);
        return jwtTokenService.validateToken(token, userDetails);
    }
}

在上述代码中,当从前端接收到一个token时,在这个方法中调用jwtTokenService的validateToken方法验证该token是否合法。

示例二

下面是一个更简单的示例,使用JAX-RS框架和Jersey实现基于token认证的方法。在 Jersey 中使用 HTTP 请求处理函数(@POST, @GET, @PUT, @DELETE) 和 Resouce 模块(@Path、@Produces、@Consumes)定义一个 API。此示例使用的是 JJWT(JSON Web Token for Java) 以及基本的 web servlet,其余的代码都是 java。

@Path("/your/resource")
public class YourResource {

    @POST
    @Path("/login")
    @Consumes(MediaType.APPLICATION_JSON)
    public Response login(User user) {
        // ...
        String jwt = createJWT(user);
        return Response.ok().entity(jwt).build();
    }

    @GET
    @Path("/secured")
    @Produces(MediaType.APPLICATION_JSON)
    public Response secureEndpoint(@HeaderParam("Authorization") String authHeader) throws JsonProcessingException {
        // 验证 token 是否正确
        JwtToken jwtToken = JwtTokenService.verifyToken(authHeader);
        if (jwtToken != null && jwtToken.getPayload().get("sub") != null) {
            // 用户重载并返回受保护的数据
            User user = retrieveUserFromDatabase(jwtToken.getPayload().get("sub").toString());
            return Response.ok().entity(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(user)).build();
        }
        return Response.status(Response.Status.UNAUTHORIZED).build();
    }

    @SuppressWarnings("deprecation")
    private String createJWT(User user) {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, 7);
        JwtBuilder builder = Jwts.builder()
                .setPayload(new Gson().toJson(user))
                .setExpiration(calendar.getTime())
                .signWith(SignatureAlgorithm.HS512, "mySecretKey");
        return builder.compact();
    }

    private User retrieveUserFromDatabase(String username) {
        // ...
        return new User();
    }
}

在上述代码中,我们定义了一个包含两个方法的类YourResource,其中:

  1. login() 方法是一个带有@POST注解的方法,用于从客户端获取用户凭证。在这个方法里,我们生成JWT并返回给客户端,客户端将此token保存在本地存储器中。
  2. secureEndpoint() 方法是一个带有@GET注解的方法,在前端发来的请求中验证此请求的 token 是否有效,如果有效,返回受保护的文本数据。在这个方法中,我们调用JwtTokenServiceverifyToken方法来验证token是否有效。

至此,我们就学习到了Java实现基于token认证的方法。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java实现基于token认证的方法示例 - Python技术站

(0)
上一篇 2023年5月19日
下一篇 2023年5月19日

相关文章

  • 详解Java中Thread 和Runnable区别

    当开发多线程程序时,Java中有两种方式可以创建线程:继承Thread类或实现Runnable接口。虽然它们最终实现的目标是相同的,但它们之间仍然存在一些重要区别。本文将详细讲解Thread和Runnable的区别,让您在编写多线程程序时选择最佳方案。 一、继承Thread类 继承Thread类是创建线程的传统方式。这是通过继承Thread类并覆盖其中的ru…

    Java 2023年5月18日
    00
  • struts1实现简单的登录功能实例(附源码)

    接下来我将详细讲解如何使用 Struts1 框架实现简单的登录功能,由于过程比较长,我将分为以下几个步骤: 准备工作 创建登录页面 编写 Action 类 配置 Struts 配置文件 运行程序,测试登录功能 1. 准备工作 首先需要准备好开发环境,本文以 Eclipse IDE 和 Tomcat 服务器作为例子。而 Struts1 的 jar 包需要我们手…

    Java 2023年5月20日
    00
  • java实现计算器加法小程序(图形化界面)

    Java实现计算器加法小程序(图形化界面) 本文将详细讲解如何使用Java语言实现一个基本的计算器加法小程序,并提供代码示例说明。以下是完整的攻略: 步骤一:创建项目 首先,我们需要创建一个Java项目,并将其命名为“calculator”。 步骤二:添加图形用户界面 我们将会使用Java Swing库来添加图形用户界面(GUI)。 我们可以通过创建一个JF…

    Java 2023年5月23日
    00
  • maven中下载jar包源码和javadoc的命令介绍

    下面我详细讲解一下 “maven中下载jar包源码和javadoc的命令介绍” 的完整攻略。 1. maven中下载jar包源码和javadoc的意义 在java开发中,使用第三方库是非常常见的事情。而有时候我们需要查看第三方库的源代码或者javadoc文档,以便更好地了解库的使用和细节。maven提供了一个方便的命令来下载jar包源代码和javadoc文档…

    Java 2023年5月26日
    00
  • Java最全文件操作实例汇总

    Java最全文件操作实例汇总 1. 文件的创建和写入 文件的创建与写入是文件操作的基础之一。使用Java可以很方便地完成这个过程。 import java.io.File; import java.io.FileWriter; import java.io.IOException; public class FileHandler { public stat…

    Java 2023年5月20日
    00
  • Java 入门图形用户界面设计之列表框JList

    下面我将详细讲解Java入门图形用户界面设计之列表框JList的完整攻略,包含以下几个方面: 列表框JList的介绍 列表框JList的基本使用方式 列表框JList的高级使用方式 示例说明 注意事项 1. 列表框JList的介绍 列表框JList是Swing组件库中的一种用于显示列表项的组件,它可以显示一个或多个列表项,并且支持单选、多选等不同的选择模式。…

    Java 2023年5月26日
    00
  • Java三种移位运算符原理解析

    Java三种移位运算符原理解析 移位运算是基于二进制补码进行运算的。Java 中有三种移位运算符:左移运算符、右移运算符和无符号右移运算符。下面分别对这三种移位运算符进行详细讲解。 左移运算符(<<) 左移运算符将一个数的二进制表示向左移动指定的位数。其语法为: 左移位数 << 左移运算值 左移位数是一个整数,代表要进行左移的位数。左…

    Java 2023年5月26日
    00
  • Java 使用多线程调用类的静态方法的示例

    Java 的多线程编程是Java中非常重要的一个概念,使用多线程技术能够提高程序的性能,同时也可以更好地利用硬件资源,扩展程序的能力。在Java中,使用多线程调用类的静态方法是一种常见的操作。下面就来详细讲解如何使用Java多线程技术调用类的静态方法。 一、创建一个继承自Thread类的子类,并实现run方法 public class MyThread ex…

    Java 2023年5月18日
    00
合作推广
合作推广
分享本页
返回顶部