SpringBoot整合Spring Security,基于JWT的认证(八)

简介: 注意要自己要在SecurityConfig里声明一下manager,service那边才能用

一、首先导入依赖

        <!--jwt依赖 -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>


二、编写工具类

package com.zimug.jwtserver.config.auth.jwt;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Data
@ConfigurationProperties(prefix = "jwt")
@Component
public class JwtTokenUtil {
    private String secret;
    private Long expiration;
    private String header;
    /**
     * 生成token令牌
     *
     * @param userDetails 用户
     * @return 令token牌
     */
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>(2);
        claims.put("sub", userDetails.getUsername());
        claims.put("created", new Date());
        return generateToken(claims);
    }
    /**
     * 从令牌中获取用户名
     *
     * @param token 令牌
     * @return 用户名
     */
    public String getUsernameFromToken(String token) {
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }
    /**
     * 判断令牌是否过期
     *
     * @param token 令牌
     * @return 是否过期
     */
    public Boolean isTokenExpired(String token) {
        try {
            Claims claims = getClaimsFromToken(token);
            Date expiration = claims.getExpiration();
            return expiration.before(new Date());
        } catch (Exception e) {
            return false;
        }
    }
    /**
     * 刷新令牌
     *
     * @param token 原令牌
     * @return 新令牌
     */
    public String refreshToken(String token) {
        String refreshedToken;
        try {
            Claims claims = getClaimsFromToken(token);
            claims.put("created", new Date());
            refreshedToken = generateToken(claims);
        } catch (Exception e) {
            refreshedToken = null;
        }
        return refreshedToken;
    }
    /**
     * 验证令牌
     *
     * @param token       令牌
     * @param userDetails 用户
     * @return 是否有效
     */
    public Boolean validateToken(String token, UserDetails userDetails) {
        String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }
    /**
     * 从claims生成令牌,如果看不懂就看谁调用它
     *
     * @param claims 数据声明
     * @return 令牌
     */
    private String generateToken(Map<String, Object> claims) {
        Date expirationDate = new Date(System.currentTimeMillis() + expiration);
        return Jwts.builder().setClaims(claims)
                .setExpiration(expirationDate)
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }
    /**
     * 从令牌中获取数据声明,如果看不懂就看谁调用它
     *
     * @param token 令牌
     * @return 数据声明
     */
    private Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }
}


三、在配置文件里配置属性

jwt:
  secret: asfdsafsdasadf
  expriation: 3600000
  header: JWTHeaderName


四、编写服务类和控制器

package com.example.jwt.config.auth.jwt;
import com.example.jwt.config.exception.CustomException;
import com.example.jwt.config.exception.CustomExceptionType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class JwtAuthService {
    @Resource
    AuthenticationManager authenticationManager;
    @Resource
    UserDetailsService userDetailsService;
    @Resource
    JwtTokenUtil jwtTokenUtil;
    /**
     * 登录认证换取JWT令牌
     * @return JWT
     */
    public String login(String username,String password) throws CustomException {
        try {
            UsernamePasswordAuthenticationToken upToken =
                    new UsernamePasswordAuthenticationToken(username, password);
            Authentication authentication = authenticationManager.authenticate(upToken);
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }catch (AuthenticationException e){
            throw new CustomException(CustomExceptionType.USER_INPUT_ERROR
                            ,"用户名或者密码不正确");
        }
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        return jwtTokenUtil.generateToken(userDetails);
    }
    public String refreshToken(String oldToken){
        if(!jwtTokenUtil.isTokenExpired(oldToken)){
            return jwtTokenUtil.refreshToken(oldToken);
        }
        return null;
    }
}


package com.example.jwt.config.auth.jwt;
import com.example.jwt.config.exception.AjaxResponse;
import com.example.jwt.config.exception.CustomException;
import com.example.jwt.config.exception.CustomExceptionType;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.Map;
@RestController
public class JwtAuthController {
    @Resource
    JwtAuthService jwtAuthService;
    @RequestMapping(value = "/authentication")
    public AjaxResponse login(@RequestBody Map<String,String> map){
        String username  = map.get("username");
        String password = map.get("password");
        if(StringUtils.isEmpty(username)
                || StringUtils.isEmpty(password)){
            return AjaxResponse.error(
                    new CustomException(CustomExceptionType.USER_INPUT_ERROR,
                    "用户名或者密码不能为空"));
        }
        try {
            return AjaxResponse.success(jwtAuthService.login(username, password));
        }catch (CustomException e){
            return AjaxResponse.error(e);
        }
    }
    @RequestMapping(value = "/refreshtoken")
    public  AjaxResponse refresh(@RequestHeader("${jwt.header}") String token){
            return AjaxResponse.success(jwtAuthService.refreshToken(token));
    }
}


放行一下接口这两个接口:

.antMatchers("/authentication","/refreshtoken").permitAll()


注意要自己要在SecurityConfig里声明一下manager,service那边才能用

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


到这里就能生成jwt令牌和刷新令牌了。


五、编写过滤器,进行鉴权

package com.example.jwt.config.auth.jwt;
import com.example.jwt.config.auth.MyUserDetailsService;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Resource
    JwtTokenUtil jwtTokenUtil;
    @Resource
    MyUserDetailsService myUserDetailsService;
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {
        //请求头里需要有token
        String jwtToken = request.getHeader(jwtTokenUtil.getHeader());
        if(!StringUtils.isEmpty(jwtToken)){
            //解密取出用户名
            String username = jwtTokenUtil.getUsernameFromToken(jwtToken);
            if(username != null &&
                    SecurityContextHolder.getContext().getAuthentication() == null){
                UserDetails userDetails = myUserDetailsService.loadUserByUsername(username);
                if(jwtTokenUtil.validateToken(jwtToken,userDetails)){
                    //给使用该JWT令牌的用户进行授权
                    UsernamePasswordAuthenticationToken authenticationToken
                            = new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                }
            }
        }
        filterChain.doFilter(request,response);
    }
}


添加过滤器在用户名密码过滤器之前:

.addFilterBefore(jwtAuthenticationTokenFilter,UsernamePasswordAuthenticationFilter.class)


六、解决跨域访问的问题

开启:

.cors()


    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("http://localhost:8080"));
        configuration.setAllowedMethods(Arrays.asList("GET","POST"));
        configuration.applyPermitDefaultValues();
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }


七、防止跨域攻击

        //仿跨站攻击,在登陆后会返回XSRF-TOKEN的cookie,在除了get请求的其他类型时均需带上 X-XSRF-TOKEN 的header,每请求一次该cookie就会刷新,下次带上新的值才能访问
       http.csrf()
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())//允许脚本读取cookie
                .ignoringAntMatchers("/authentication")


然后写个控制器测试下:

    @RequestMapping(value = "/hello")
    public String hello() {
        return "world";
    }


可以看到get请求时不需要X-XSRF-TOKEN请求头

20200925121829256.png


换成post请求,勾上xsrf请求头,还是403了,因为这个是之前的了,我们要把最新的复制过去:

2020092512195237.png


换了后,就成功了:

20200925122036434.png

目录
相关文章
|
1月前
|
XML JavaScript Java
SpringBoot集成Shiro权限+Jwt认证
本文主要描述如何快速基于SpringBoot 2.5.X版本集成Shiro+JWT框架,让大家快速实现无状态登陆和接口权限认证主体框架,具体业务细节未实现,大家按照实际项目补充。
83 11
|
1月前
|
缓存 安全 Java
Spring Boot 3 集成 Spring Security + JWT
本文详细介绍了如何使用Spring Boot 3和Spring Security集成JWT,实现前后端分离的安全认证概述了从入门到引入数据库,再到使用JWT的完整流程。列举了项目中用到的关键依赖,如MyBatis-Plus、Hutool等。简要提及了系统配置表、部门表、字典表等表结构。使用Hutool-jwt工具类进行JWT校验。配置忽略路径、禁用CSRF、添加JWT校验过滤器等。实现登录接口,返回token等信息。
359 12
|
2月前
|
JSON 安全 Java
什么是JWT?如何使用Spring Boot Security实现它?
什么是JWT?如何使用Spring Boot Security实现它?
476 5
|
3月前
|
JSON 安全 算法
Spring Boot 应用如何实现 JWT 认证?
Spring Boot 应用如何实现 JWT 认证?
108 8
|
3月前
|
JSON 安全 数据安全/隐私保护
Python认证新风尚:OAuth遇上JWT,安全界的时尚Icon👗
在当今互联网世界中,数据安全和隐私保护至关重要。Python 作为 Web 开发的主流语言,其认证机制也在不断进步。OAuth 2.0 和 JSON Web Tokens (JWT) 是当前最热门的安全认证方案,不仅保障数据安全传输,还简化用户认证流程。本文介绍如何在 Python 中结合 OAuth 2.0 和 JWT,打造一套既安全又高效的认证体系。通过 Flask-HTTPAuth 和 PyJWT 等库,实现授权和验证功能,确保每次请求的安全性和便捷性。
60 3
|
3月前
|
JavaScript NoSQL Java
CC-ADMIN后台简介一个基于 Spring Boot 2.1.3 、SpringBootMybatis plus、JWT、Shiro、Redis、Vue quasar 的前后端分离的后台管理系统
CC-ADMIN后台简介一个基于 Spring Boot 2.1.3 、SpringBootMybatis plus、JWT、Shiro、Redis、Vue quasar 的前后端分离的后台管理系统
90 0
|
3月前
|
JSON 算法 安全
JWT Bearer 认证在 .NET Core 中的应用
【10月更文挑战第30天】JWT(JSON Web Token)是一种开放标准,用于在各方之间安全传输信息。它由头部、载荷和签名三部分组成,用于在用户和服务器之间传递声明。JWT Bearer 认证是一种基于令牌的认证方式,客户端在请求头中包含 JWT 令牌,服务器验证令牌的有效性后授权用户访问资源。在 .NET Core 中,通过安装 `Microsoft.AspNetCore.Authentication.JwtBearer` 包并配置认证服务,可以实现 JWT Bearer 认证。具体步骤包括安装 NuGet 包、配置认证服务、启用认证中间件、生成 JWT 令牌以及在控制器中使用认证信息
175 2
|
4月前
|
JSON 安全 算法
|
4月前
|
存储 安全 Java
|
4月前
|
JSON 安全 数据安全/隐私保护
Python认证新风尚:OAuth遇上JWT,安全界的时尚Icon👗
【10月更文挑战第2天】在当今互联网世界中,数据安全与隐私保护日益重要。Python 作为广泛应用于 Web 开发的语言,其认证机制也不断进化。OAuth 2.0 和 JSON Web Tokens (JWT) 成为当前最热门的安全认证方案,不仅保障数据安全传输,还简化了用户认证流程。本文将介绍 Python 如何结合 OAuth 2.0 和 JWT 打造安全高效的认证体系。
51 3