jwt+token验证

jwt结构

一个 JWT 实际上就是一个字符串,它由三部分组成:头部、载荷与签名。中间用点 . 分隔成三个部分。

头部 / header

header 由两部分组成: token 的类型 JWT 和算法名称:HMAC、SHA256、RSA

载荷 / Payload

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 指定七个默认字段供选择。
除了默认字段之外,你完全可以添加自己想要的任何字段,一般用户登录成功后,就将用户信息存放在这里

签名 / Signature
签名部分是对上面的 头部、载荷 两部分数据进行的数据签名
为了保证数据不被篡改,则需要指定一个密钥,而这个密钥一般只有你知道,并且存放在服务端

token认证过程

在这里插入图片描述
SpringSecurity的原理其实就是一个过滤器链,内部包含了提供各种功能的过滤器

springboot实现jwt+token认证

导入依赖

               <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

在config层中建立WebSecurityConfig加入自定义拦截器与自定义异常处理

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder
                .userDetailsService(this.userDetailsService)
                .passwordEncoder(passwordEncoder());
    }

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

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

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

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                //token的验证方式不需要开启csrf的防护
                .csrf().disable()
                .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
                .and()
                //设置无状态的连接,即不创建session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
//                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
//                当前的url允许进行匿名访问,即不需要身份认证
                .antMatchers(
                        "/",
                        "/*.html",
                        "/favicon.ico",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js"
                ).permitAll()
                //配置swagger界面的匿名访问
                .antMatchers("/swagger-ui.html",
                        "/swagger-ui/*",
                        "/swagger-resources/**",
                        "/v2/api-docs",
                        "/v3/api-docs",
                        "/webjars/**").permitAll()
                .antMatchers("/data/**").permitAll()
                .antMatchers("/image/**").permitAll()
                .antMatchers("/configuration/ui").permitAll()
                .antMatchers("/configuration/security").permitAll()


                //配置允许匿名访问的路径
                .antMatchers("/nb5/**").permitAll()
                .antMatchers("/webapp/**").permitAll()
                .antMatchers("/home/**").permitAll()
                .antMatchers("/nb4/**").permitAll()
                 .antMatchers("/shale_porosity/user/log").permitAll()
                 .antMatchers("/shale_porosity/upload/**").permitAll()
                .antMatchers("/shale_porosity/download/**").permitAll()
                .antMatchers("/shale_porosity/task/**").permitAll()
                 .antMatchers(HttpMethod.POST,"/wx_official_accounts/record-time").permitAll()
                .anyRequest().authenticated();   //除了上面请求外都需要权限认证

        //添加jwt filter
        httpSecurity
                .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);

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

在security中建立JwtAuthenticationTokenFilter拦截器与JwtAuthenticationEntryPoint异常处理

@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    private String tokenHeader = "Authorization";


    @Override
    //允许请求头 设置
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        if ("OPTIONS".equals(httpServletRequest.getMethod())) {
            log.info("浏览器的请求预处理");
            httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
//            httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS,DELETE,PUT");
            httpServletResponse.setHeader("Access-Control-Allow-Methods", "*");
            httpServletResponse.setHeader("Access-Control-Max-Age", "3600");
            httpServletResponse.setHeader("Access-Control-Allow-Headers", "Origin,X-Requested-With,Content-Type,Accept,Authorization,token,Cookie");
        } else {
            String requestUrl = httpServletRequest.getRequestURI();
            log.info("requestURI: {}", requestUrl);
            String authtoken = httpServletRequest.getHeader(this.tokenHeader);
            System.out.println(authtoken);
            String workNum = jwtTokenUtil.getUsernameFromToken(authtoken);
            //取出username  (workNum)

            log.info("checking authentication for user " + workNum);

            if (workNum != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                //请求头
                log.info("token中的username不为空,Context中的authentication为空时,进行token验证");

                UserDetails userDetails = this.userDetailsService.loadUserByUsername(workNum);
                log.info("加载UserDetails:{}", userDetails.getUsername());
                if (jwtTokenUtil.validateToken(authtoken, userDetails)) {
                    //验证token中的信息,调用了tokenutils的方法
                    UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(null, null, null);
                    System.out.println(authenticationToken);
                    log.info("authenticated user" + workNum + ", setting security context");
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                }
            }
            filterChain.doFilter(httpServletRequest, httpServletResponse);
        }
    }
}

@Component
@Slf4j
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {

    private static final long serialVersionUID = -8970718410437077606L;

    @Override
    public void commence(HttpServletRequest httpServletRequest,
						 HttpServletResponse httpServletResponse,
						 AuthenticationException e) throws IOException, ServletException {
        httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("text/html; charset=utf-8");
        R result = R.error(ResultEnum.AUTHENTICATION_ERROR);
        log.info("需要身份认证:{}", result);
        httpServletResponse.getWriter().append(JSON.toJSONString(result));
    }
}

期间需要注意的一些事情

登录

​ 自定义登录接口

​ 调用方法进行认证 如果认证通过生成jwt返回前端,并将用户信息存入 SecurityContextHolder中,SecurityContextHolder用Threadlocal方式实现,用于存储线程中独有的信息。

校验:

​ 定义Jwt认证过滤器

​ 获取token

​ 解析token获取其中的userid

​ 判断SecurityContextHolder中是否还存在值,如果不存在,通过userid查找数据库中的user,并判断token是否过期,如果通过认证,会将新获取的用户信息存入SecurityContextHolder中,后序需要用到用户信息可直接在里面读取。

权限判断

在config里面创建 WebConfig

@Configuration
public class WebConfig implements WebMvcConfigurer {


    @Autowired
    private AuthRoleInterceptor authRoleInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //除了/user其他都需要输入返回的   token     判断权限Role
        registry.addInterceptor(authRoleInterceptor)
                .excludePathPatterns("/user/**");
    }
}

在security中创建权限拦截器

@Slf4j
@Service
public class AuthRoleInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private UserService userService;

    @Override

    //HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,
    // 通过这个对象提供的方法,可以获得客户端请求的所有信息。
    //在调用逻辑处理方法前面进行判断权限   preHandle获取请求信息判断用户的权限
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println(".....................");
        response.setCharacterEncoding("UTF-8");
        //此方法同时设置服务器和客户端都是用UTF-8字符集,还设置了响应头
        response.setContentType("text/html;charset=utf-8");

        String json = JSON.toJSONString(R.error(ResultEnum.AUTHENTICATION_ERROR));
        //获取当前登录的用户信息
        User user = userService.getCurrentUser();
        if (user == null) {
            return true;
        }
        
        log.info("============执行权限验证============");
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            RoleContro roleContro = handlerMethod.getMethodAnnotation(RoleContro.class);
            if (roleContro == null) {
                return true;
            }
            Integer roleValue = roleContro.role().getValue();
            Integer userValue = user.getRole();
            log.info("RoleValue:{},userRole:{}", roleValue, userValue);
            if (userValue >= roleValue) {
                return true;
            } else {
                json = JSON.toJSONString(R.error(ResultEnum.PERMISSION_DENNY));
                log.info("============权限不足===============");
            }
        }
        response.getWriter().append(json);
        return false;
    }
}

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值