springboot基于token实现登录认证

最近因为项目需求,需采用token的方式实现登录认证,而不再使用session的方式登录,因而采用springboot集成JWT生成token实现登录认证。

1.首先添加jwt所需jar包

<dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt</artifactId>
      <version>0.7.0</version>
</dependency>

2.接下来根据需要建一个工具类JwtUtils。

/**
 * jwt工具类
 */
@ConfigurationProperties(prefix = "dingapp.jwt")
@Component
public class JwtUtils {
    private Logger logger = LoggerFactory.getLogger(getClass());

    private String secret;
    private long expire;
    private String header;

    /**
     * 生成jwt token
     */
    public String generateToken(String companyId,String userId,Long expireTime, YAMLUtils.SysType st) {
        Date nowDate = new Date();
        Long v = expire;
        if(expireTime != null){
            v = expireTime;
        }
        //过期时间
        Date expireDate = new Date(nowDate.getTime() + v * 1000);
        return Jwts.builder().claim("SysType",st.getType())
                .setHeaderParam("typ", "JWT")
                .setId(companyId)
                .setSubject(userId)
                .setIssuedAt(nowDate)
                .setExpiration(expireDate)
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }
    /**
     * 解析token  
     */
    public Claims getClaimByToken(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        }catch (Exception e){
            logger.debug("validate is token error ", e);
            return null;
        }
    }

    /**
     * token是否过期
     * @return  true:过期
     */
    public boolean isTokenExpired(Date expiration) {
        return expiration.before(new Date());
    }

    public String getSecret() {
        return secret;
    }

    public void setSecret(String secret) {
        this.secret = secret;
    }

    public long getExpire() {
        return expire;
    }

    public void setExpire(long expire) {
        this.expire = expire;
    }

    public String getHeader() {
        return header;
    }

    public void setHeader(String header) {
        this.header = header;
    }

这个工具类是通过jwt生成token以及对token的解析,这里对token设置了过期时间,如果token解析不出或者说过期则会抛出异常。
3.工具类写好后,可以在登录时验证完登录密码生成token并返回给前端。

    @PostMapping("/login")
    public ApiResult login(@RequestBody SysUser user, HttpServletRequest request) {
    	ApiResult result = new ApiResult();
        //多类型账号登陆
        SysUser userBySomeCase = userService.getUserByAcc(user);
        Integer landType = user.getType();
        SysUser u;
        u = new SysUser();
        u.setAccNum(user.getAccNum());
        u = userService.getUserByAcc(u);
        if(u != null && !Objects.equals(u.getPasswd(),CommonUtil.encryptPsw(user.getPasswd()))){
                return result.failed("账号或密码错误");
            }
       }
       if(u == null){
           return result.failed("账号或密码错误");
        }
        SysUser data = u;
        //
        String token = jwtUtils.generateToken(null,u.getUserID()+"",null,YAMLUtils.SysType.SysType3);
        data.setToken(token1);
        return result.success(data, "登录成功");
    }

4.当用户请求接口时会在header中携带token,后端会对所有接口在前置拦截器进行拦截,作用是对拿到的token进行解析,如果解析失败,会抛出SignatureException异常,如果解析成功,则不会拦截。

 @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        String token = httpServletRequest.getHeader("token");
        if(StringUtils.isBlank(token )){
        	throw new IncorrectCredentialsException("请先登录");
        }
        final Claims claims = jwtUtils.getClaimByToken(token);
        if (claims == null || jwtUtils.isTokenExpired(claims.getExpiration())) {
            throw new IncorrectCredentialsException("token失效,请重新登录");
        }      
        return true;
    }

5.可以对jwt进行全局配置。

dingapp:
  jwt:
    # 加密秘钥
    secret: dfjlaer********
    # token有效时长,24小时,单位秒
    expire: 86400
    header: token`在这里插入代码片`

总结:
1.因为jwt生成token的无状态性,以及便于传输,构成非常简单,字节占用很小,它不需要在服务端保存
会话信息, 所以它易于应用的扩展。
2.但一旦拿到访问的token,就可直接访问到服务器,所以对于私钥的保护非常重要。

可以使用Spring Security和JWT(JSON Web Token)来实现基于token登录验证接口。下面是一个简单的示例: 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.0</version> </dependency> ``` 2. 配置Spring Security 在Spring Boot的配置类添加以下代码: ``` @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsServiceImpl userDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/api/auth/**").permitAll() .anyRequest().authenticated() .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } @Bean public JwtAuthenticationFilter authenticationJwtTokenFilter() { return new JwtAuthenticationFilter(); } } ``` 这个配置类禁用了CSRF保护,允许所有用户访问“/api/auth/**”接口,其余接口需要进行认证。它还使用“JwtAuthenticationFilter”类来验证token。 3. 实现UserDetailsService 创建一个UserDetailsService的实现类,用于从数据库或其他数据源获取用户信息。例如: ``` @Service public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private UserRepository userRepository; @Override @Transactional public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username) .orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username)); return UserDetailsImpl.build(user); } } ``` 这里的“UserDetailsImpl”类是一个自定义的实现了UserDetails接口的类,用于封装用户信息。 4. 实现JwtUtils 创建一个JwtUtils类,用于生成和验证token。例如: ``` @Component public class JwtUtils { @Value("${jwt.secret}") private String jwtSecret; @Value("${jwt.expirationMs}") private int jwtExpirationMs; public String generateJwtToken(Authentication authentication) { UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal(); return Jwts.builder() .setSubject((userPrincipal.getUsername())) .setIssuedAt(new Date()) .setExpiration(new Date((new Date()).getTime() + jwtExpirationMs)) .signWith(SignatureAlgorithm.HS512, jwtSecret) .compact(); } public boolean validateJwtToken(String authToken) { try { Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken); return true; } catch (SignatureException e) { logger.error("Invalid JWT signature: {}", e.getMessage()); } catch (MalformedJwtException e) { logger.error("Invalid JWT token: {}", e.getMessage()); } catch (ExpiredJwtException e) { logger.error("JWT token is expired: {}", e.getMessage()); } catch (UnsupportedJwtException e) { logger.error("JWT token is unsupported: {}", e.getMessage()); } catch (IllegalArgumentException e) { logger.error("JWT claims string is empty: {}", e.getMessage()); } return false; } public String getUsernameFromJwtToken(String token) { return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getSubject(); } } ``` 这里的“jwt.secret”和“jwt.expirationMs”分别是用于生成token的密钥和过期时间(以毫秒为单位)。 5. 实现JwtAuthenticationFilter 创建一个JwtAuthenticationFilter类,用于在每个请求上验证token。例如: ``` public class JwtAuthenticationFilter extends OncePerRequestFilter { @Autowired private JwtUtils jwtUtils; @Autowired private UserDetailsServiceImpl userDetailsService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { String jwt = parseJwt(request); if (jwt != null && jwtUtils.validateJwtToken(jwt)) { String username = jwtUtils.getUsernameFromJwtToken(jwt); UserDetails userDetails = userDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } } catch (Exception e) { logger.error("Cannot set user authentication: {}", e.getMessage()); } filterChain.doFilter(request, response); } private String parseJwt(HttpServletRequest request) { String headerAuth = request.getHeader("Authorization"); if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) { return headerAuth.substring(7, headerAuth.length()); } return null; } } ``` 这个过滤器将从请求头获取token,并使用JwtUtils类来验证token。如果token有效,则将用户信息设置为Spring Security的上下文认证信息。 6. 创建登录接口 创建一个登录接口,用于验证用户并生成token。例如: ``` @RestController @RequestMapping("/api/auth") public class AuthController { @Autowired private AuthenticationManager authenticationManager; @Autowired private JwtUtils jwtUtils; @PostMapping("/signin") public ResponseEntity<?> authenticateUser(@Valid @RequestBody LoginRequest loginRequest) { Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword())); SecurityContextHolder.getContext().setAuthentication(authentication); String jwt = jwtUtils.generateJwtToken(authentication); return ResponseEntity.ok(new JwtResponse(jwt)); } } ``` 这个接口将使用AuthenticationManager来验证用户信息,并使用JwtUtils类来生成token。如果验证成功,则返回一个包含tokenJwtResponse对象。 7. 测试接口 现在可以测试接口了。使用POST方法向“/api/auth/signin”发送以下JSON数据: ``` { "username": "your_username", "password": "your_password" } ``` 如果验证成功,则会返回一个包含token的JSON响应。以后,可以在每个请求token作为“Authorization”请求头的值发送。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜空下的星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值