使用SpringSecurity为API接口添加鉴权token

当我们使用SpringBoot实现了一个简单的API接口之后,我们如何去保证我们的API接口只让我们运行的人调用呢。这时候就需要对我们的API接口进行保护。在别人访问这些接口的时候,我们对访问者进行身份的验证,从而对接口的保护。

基本流程如下图:

当然我们需要一个接口给用户请求Token,要不然用户拿不到token怎么去请求其他资源呢。流程图如下:

接下来我们按照流程一步一步实现。

首先实现请求token

    @GetMapping("/get_token")
    public JsonResult getToken(@RequestParam String username,@RequestParam String password){
        //apiUser通过用户url传入的账号和密码
        ApiUser apiUser=loginService.loginApiUser(username);
        //这里验证用户的信息是否正确
        JsonResult jsonResult=checkApiUser(apiUser,password);
        //如果正确那么返回Null,不正确返回提示然后显示给用户
        if (jsonResult!=null){
            return jsonResult;
        }
        //到这里证明用户信息是正确的,那么我们进行token的生成然后返回给用户
        String token=loginService.generateToken(apiUser);

        return JsonResult.suc(token);
    }
  1. 通过url拿到用户传过来的数据,账户和密码
  2. 对账户密码进行验证,如果不正确返回错误提示。
  3. 如果正确进行token生存,然后返回给用户

那么我们如何验证呢

  private JsonResult checkApiUser(ApiUser apiUser,String password){
        if (apiUser==null){
            return JsonResult.error(434,"账户不存在");
        }else {
            if (apiUser.getEnable()==0){
                return JsonResult.error(452,"账户在黑名单");
            }
            if (!apiUser.getPassword().equals(password)){
                //equals相等返回true
                return JsonResult.error(452,"账户密码错误");

            }

        }
        return null;
    }
  1. 先通过用户传入的用户名进行查找,看是否存在该用户。
  2. 如果存在该用户,判断用户的状态是否可用。
  3. 如果状态可用,那么比较数据库保存的密码和用户输入的密码是否匹配。

那么如何生成token呢

 public String generateToken(ApiUser tokenDetail) {
        Map<String, Object> claims = new HashMap<String, Object>();
        claims.put("sub", tokenDetail.getUsername());
        claims.put("created", this.generateCurrentDate());
        return this.generateToken(claims);
    }

    /**
     * 根据 claims 生成 Token
     *
     * @param claims
     * @return
     */
    private String generateToken(Map<String, Object> claims) {
        logger.info("成功进入生产token",claims);
        try {
            return Jwts.builder()
                    .setClaims(claims)
                    .setExpiration(this.generateExpirationDate())
                    .signWith(SignatureAlgorithm.HS512, this.secret.getBytes("UTF-8"))
                    .compact();
        } catch (UnsupportedEncodingException ex) {
            //didn't want to have this method throw the exception, would rather log it and sign the token like it was before
            logger.warn(ex.getMessage());
            return Jwts.builder()
                    .setClaims(claims)
                    .setExpiration(this.generateExpirationDate())
                    .signWith(SignatureAlgorithm.HS512, this.secret)
                    .compact();
        }
    }
  1. 我们通过JWT工具去进行token的生成。(如果不了解JWT可用百度看看,非常简单。)
  2. 通过用户信息,我们生成一个token并返回给客户端。
  3. 用户拿着这个token去请求资源,就可以了。

接下来我们看用户请求资源是如何实现的

 @PostMapping("/courseList")
    public JsonResult getAllCourse(Page<Course> page, Integer state){
        //获取当前学年的课程列表

        return JsonResult.suc(courseService.getAll(page,state));
    }

这是一个资源API接口,那么用户在访问这个接口的时候我们就需要对身份进行验证,那么是在哪里验证的呢

@Configuration      // 声明为配置类
@EnableWebSecurity      // 启用 Spring Security web 安全的功能
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 注册 401 处理器
     */
    @Autowired
    private EntryPointUnauthorizedHandler unauthorizedHandler;

    /**
     * 注册 403 处理器
     */
    @Autowired
    private MyAccessDeniedHandler accessDeniedHandler;

    /**
     * 注册 token 转换拦截器为 bean
     * 如果客户端传来了 token ,那么通过拦截器解析 token 赋予用户权限
     *
     * @return
     * @throws Exception
     */
    @Bean
    public AuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
        AuthenticationTokenFilter authenticationTokenFilter = new AuthenticationTokenFilter();
        authenticationTokenFilter.setAuthenticationManager(authenticationManagerBean());
        return authenticationTokenFilter;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/get_token").permitAll()     // 所有人可以访问
                .anyRequest().authenticated()       // 必须携带token
                .and()
                // 配置被拦截时的处理
                .exceptionHandling()
                .authenticationEntryPoint(this.unauthorizedHandler)   // 添加 token 无效或者没有携带 token 时的处理
                .accessDeniedHandler(this.accessDeniedHandler)      //添加无权限时的处理
                .and()
                .csrf()
                .disable()                      // 禁用 Spring Security 自带的跨域处理
                .sessionManagement()                        // 定制我们自己的 session 策略
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 调整为让 Spring Security 不创建和使用 session


        /**
         * 本次 json web token 权限控制的核心配置部分
         * 在 Spring Security 开始判断本次会话是否有权限时的前一瞬间
         * 通过添加过滤器将 token 解析,将用户所有的权限写入本次会话
         */
        http
                .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
    }
}
  1. 首先我们通过SpringSecurity进行url配置,也就是给URL加了过滤器。
  2. 当用户访问需要token的资源路径的时候就会触发过滤器。
  3. http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);这一句相当于把我们自定义的过滤器加入到Springsecurity过滤器链中。
 @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 将 ServletRequest 转换为 HttpServletRequest 才能拿到请求头中的 token
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        // 尝试获取请求头的 token
        String authToken =httpRequest.getHeader(this.tokenHeader);//获取token=xxx

        // 尝试拿 token 中的 username
        // 若是没有 token 或者拿 username 时出现异常,那么 username 为 null
        String username = this.tokenUtils.getUsernameFromToken(authToken);

        // 如果上面解析 token 成功并且拿到了 username 并且本次会话的权限还未被写入
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            // 用 UserDetailsService 从数据库中拿到用户的 UserDetails 类
            // UserDetails 类是 Spring Security 用于保存用户权限的实体类
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
            // 检查用户带来的 token 是否有效
            // 包括 token 和 userDetails 中用户名是否一样, token 是否过期, token 生成时间是否在最后一次密码修改时间之前
            // 若是检查通过
            if (this.tokenUtils.validateToken(authToken, userDetails)) {
                // 生成通过认证
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
                // 将权限写入本次会话
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
            if (!userDetails.isEnabled()){
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/json;charset=UTF-8");
                response.getWriter().print("{\"code\":\"452\",\"data\":\"\",\"message\":\"账号处于黑名单\"}");
                return;
            }
        }

        chain.doFilter(request, response);
    }
  1. 在自定义过滤器中通过 String authToken =httpRequest.getHeader(this.tokenHeader);这一句代码我们获得了用户传入的token值。
  2. 通过对token值的解析我们可以获取到用户信息,这里的信息在是在生成token的时候我们加入到token中的。(获取到用户信息你可以对用户操作进行一些记录。)同时如果获取的用户信息不存在我们数据库中,那么证明该token不是正确的,不在进行后面的业务逻辑
  3. 用户信息正确,我们对token进行验证看token是否过期,或者用户是否已经被禁用了。
    this.tokenUtils.validateToken(authToken, userDetails)这一句进行验证

这样完整的鉴权流程我们就实现了。上面只是部分代码。

源码:https://github.com/xushuoAI/Springboot-SpringSecurity-Mybatis-Redis-

以上代码参考了许多博主的博文。非常感谢各位前辈的分享。

### 回答1: Spring Security是一个用于为Java应用程序提供身份认证和访问控制的安全框架。 在Spring Security中,接口资源的访问权限通常是通过在接口使用注解来实现的。 例如,使用`@PreAuthorize`注解可以在接口调用之前进行权限验证,只有当认证用户具有指定权限时,才能访问该接口。 例如: ``` @PreAuthorize("hasAuthority('ROLE_ADMIN')") @GetMapping("/api/admin/users") public List<User> getAllUsers() { // implementation } ``` 在这个例子中,只有具有`ROLE_ADMIN`角色的用户才能访问`/api/admin/users`接口。 ### 回答2: Spring Security是一个功能强大的开源框架,主要用于实现应用程序的身份认证和授权功能。其中,接口资源鉴权Spring Security的一个重要功能。 Spring Security提供了丰富的API配置选项,可以灵活地定义接口资源的访问权限。下面是使用Spring Security进行接口资源鉴权的一般步骤: 1. 引入Spring Security依赖:在项目的配置文件中添加Spring Security的依赖项,以便使用Spring Security相关的类和接口。 2. 配置SecurityConfig类:创建一个SecurityConfig类并继承自WebSecurityConfigurerAdapter,用于配置鉴权相关的信息。在该类中可以定义认证方式、授权规则等。 3. 实现UserDetailsService接口:创建一个类实现UserDetailsService接口,并重写loadUserByUsername方法来自定义用户的认证方式。在该方法中可以根据用户名从数据库或其他数据源中获取用户的详细信息,并返回一个UserDetails对象。 4. 定义授权规则:通过SecurityConfig类的configure方法,使用antMatchers等方法来定义接口的访问权限。可以根据URL、请求方法等多个条件来进行配置,如将某些接口限制为只有管理员角色可以访问。 5. 启用Spring Security:在项目的配置文件中启用Spring Security,可以通过注解@EnableWebSecurity来启用Spring Security功能。 通过以上步骤的配置Spring Security会在请求接口时对用户进行身份认证,并根据配置的授权规则判断用户是否有访问该接口的权限。如果用户没有权限,则会返回相应的错误信息或跳转到指定的页面。 总结来说,Spring Security接口资源鉴权功能可以通过配置SecurityConfig类和实现UserDetailsService接口来自定义认证和授权规则,从而保证应用程序的接口访问权限的安全性。 ### 回答3: Spring Security是一个用于处理认证和授权的框架,接口资源鉴权是其中的一个重要功能。 首先,Spring Security提供了一种灵活的方式去定义和管理用户的认证信息。它支持多种认证方式,包括基于表单、基于HTTP Basic Auth、基于JSON Web TokenJWT)等。用户在访问接口资源前,需要进行身份认证,通过用户名密码等信息进行验证。 其次,Spring Security还提供了一套丰富的授权机制,可以细粒度地控制用户对接口资源的访问权限。可以通过注解、配置文件或者自定义实现的方式,来定义接口资源的访问规则。例如,可以在控制器的方法上添加`@PreAuthorize`注解,指定需要具备的角色或权限才能访问该接口。 在接口资源鉴权过程中,Spring Security还提供了一些常用的特性,例如CSRF(跨站请求伪造)防护、Session管理,以及日志记录等。这些功能可以帮助开发者更好地保护接口资源的安全性和完整性。 总结来说,Spring Security接口资源鉴权是通过认证和授权来保护接口资源的安全性。它提供了灵活的认证方式和丰富的授权机制,可以根据应用的需求进行定制化配置。通过使用Spring Security,我们能够更好地确保接口资源只被合法用户访问,并保护用户数据的安全。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值