• spring-security实现的token授权


    在我的用户密码授权文章里介绍了spring-security的工作过程,不了解的同学,可以先看看用户密码授权这篇文章,在
    用户密码授权模式里,主要是通过一个登陆页进行授权,然后把授权对象写到session里,它主要用在mvc框架里,而对于webapi来说,一般不会采用这种方式,对于webapi
    来说,一般会用jwt授权方式,就是token授权码的方式,每访问api接口时,在http头上带着你的token码,而大叔自己也写了一个简单的jwt授权模式,下面介绍一下。

    WebSecurityConfig授权配置

    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class TokenWebSecurityConfig extends WebSecurityConfigurerAdapter {
      /**
       * token过滤器.
       */
      @Autowired
      LindTokenAuthenticationFilter lindTokenAuthenticationFilter;
    
      @Bean
      @Override
      public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
      }
    
      @Override
      protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
            .csrf().disable()
            // 基于token,所以不需要session
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            .authorizeRequests()
            // 对于获取token的rest api要允许匿名访问
            .antMatchers("/lind-auth/**").permitAll()
            // 除上面外的所有请求全部需要鉴权认证
            .anyRequest().authenticated();
        httpSecurity
            .addFilterBefore(lindTokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        // 禁用缓存
        httpSecurity.headers().cacheControl();
      }
    
      /**
       * 密码生成策略.
       *
       * @return
       */
      @Bean
      public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
      }
    }
    
    

    授权接口login

    对外开放的,需要提供用户名和密码为参数进行登陆,然后返回token码,当然也可以使用手机号和验证码登陆,授权逻辑是一样的,获取用户信息都是使用UserDetailsService,
    然后开发人员根据自己的业务去重写loadUserByUsername来获取用户实体。

    用户登陆成功后,为它授权及认证,这一步我们会在redis里建立token与用户名的关系。

    @GetMapping(LOGIN)
      public ResponseEntity<?> refreshAndGetAuthenticationToken(
          @RequestParam String username,
          @RequestParam String password) throws AuthenticationException {
        return ResponseEntity.ok(generateToken(username, password));
      }
    
      /**
       * 登陆与授权.
       *
       * @param username .
       * @param password .
       * @return
       */
      private String generateToken(String username, String password) {
        UsernamePasswordAuthenticationToken upToken = new UsernamePasswordAuthenticationToken(username, password);
        // Perform the security
        final Authentication authentication = authenticationManager.authenticate(upToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // Reload password post-security so we can generate token
        final UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        // 持久化的redis
        String token = CommonUtils.encrypt(userDetails.getUsername());
        redisTemplate.opsForValue().set(token, userDetails.getUsername());
        return token;
      }
    

    LindTokenAuthenticationFilter代码

    主要实现了对请求的拦截,获取http头上的Authorization元素,token码就在这个键里,我们的token都是采用通用的Bearer开头,当你的token没有过期时,会
    存储在redis里,key就是用户名的md5码,而value就是用户名,当拿到token之后去数据库或者缓存里拿用户信息进行授权即可。

    /**
     * token filter bean.
     */
    @Component
    public class LindTokenAuthenticationFilter extends OncePerRequestFilter {
    
      @Autowired
      RedisTemplate<String, String> redisTemplate;
      String tokenHead = "Bearer ";
      String tokenHeader = "Authorization";
      @Autowired
      private UserDetailsService userDetailsService;
    
      /**
       * token filter.
       *
       * @param request     .
       * @param response    .
       * @param filterChain .
       */
      @Override
      protected void doFilterInternal(
          HttpServletRequest request,
          HttpServletResponse response,
          FilterChain filterChain) throws ServletException, IOException {
    
        String authHeader = request.getHeader(this.tokenHeader);
        if (authHeader != null && authHeader.startsWith(tokenHead)) {
          final String authToken = authHeader.substring(tokenHead.length()); // The part after "Bearer "
          if (authToken != null && redisTemplate.hasKey(authToken)) {
            String username = redisTemplate.opsForValue().get(authToken);
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
              UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
              //可以校验token和username是否有效,目前由于token对应username存在redis,都以默认都是有效的
              UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                  userDetails, null, userDetails.getAuthorities());
              authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(
                  request));
              logger.info("authenticated user " + username + ", setting security context");
              SecurityContextHolder.getContext().setAuthentication(authentication);
            }
          }
        }
    
        filterChain.doFilter(request, response);
    
      }
    

    测试token授权

    get:http://localhost:8080/lind-demo/login?username=admin&password=123
    
    post:http://localhost:8080/lind-demo/user/add
    Content-Type:application/json
    Authorization:Bearer 21232F297A57A5A743894A0E4A801FC3
    
  • 相关阅读:
    DELPHI 各版本下载
    一个好的网站,学习前端
    没那么难,谈CSS的设计模式
    一个前端的自我修养
    如何学习Javascript
    jQuery WeUI V0.4.2 发布
    微信官方开源UI库-WeUI
    js与php传递参数
    ?js调用PHP里的变量,怎么弄?
    Liferay7 BPM门户开发之23: 了解内置工作流(Kaleo Workflow)
  • 原文地址:https://www.cnblogs.com/lori/p/10538592.html
Copyright © 2020-2023  润新知