技术交流


springboot集成Security使用token实现认证授权(二) 认证授权

Apr 15, 2020 10:55:04 PM
47
2

......接上文“springboot集成Security使用token实现认证授权(一) 准备篇”


security的认证授权

配置类:

@Configuration
//开启security
@EnableWebSecurity
//开启方法级注解支持
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Autowired
    private RsaProperties rsaProperties;

    /**
     * 加密对象放入ioc容器
     *
     * @return
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }


    /**
     * 加密策略
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder());
    }

    /**
     * 全新啊配置
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                //.antMatchers().permitAll()
                //需认证后访问的方法
                .anyRequest()
                .authenticated()
                .and()
                //认证过滤器
                .addFilter(new UserAuthenticationFilter(authenticationManager(), rsaProperties))
                //token验证过滤器
                .addFilter(new TokenVerifyFilter(authenticationManager(), rsaProperties))
                //禁用session
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                //关闭csrf
                .csrf().disable()
                .cors().disable()
                ;
    }
}

 

认证过滤器:

public class UserAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private AuthenticationManager authenticationManager;

    private RsaProperties rsaProperties;

    public UserAuthenticationFilter(AuthenticationManager authenticationManager, RsaProperties rsaProperties) {
        this.authenticationManager = authenticationManager;
        this.rsaProperties = rsaProperties;
    }

    /**
     * 获取用户名密码
     *
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        UserPo userPo = null;
        try {
            //获取用户信息
            userPo = new ObjectMapper().readValue(request.getInputStream(), UserPo.class);
            //认证
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(userPo.getUsername(), userPo.getPassword());
            Authentication authentication = authenticationManager.authenticate(authRequest);
            return authentication;
        } catch (IOException e) {
            DataToHttpServletResponseUtil.msgToResponseWhenSuccess(response, JsonResponseUtil.fail(ResponseCodeAndMsgEnum.FAILE.getCode(), "用户名或密码错误"));
        }
        throw new BaseException("登录异常");
    }

    /**
     * 用户名密码授权成功返货token
     *
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        //authResult获取认证成功的用户信息
        UserPo authUser = (UserPo) authResult.getPrincipal();
        authUser.setPassword(null);
        //生成token
        String token = JwtUtil.generateTokenExpireInMinutes(authUser, rsaProperties.getPrivateKey(), 10);
        //token写入header
        response.addHeader("Authorization", "Bearer " + token);
        DataToHttpServletResponseUtil.msgToResponseWhenSuccess(response, JsonResponseUtil.success(ResponseCodeAndMsgEnum.SUCCESS.getCode(), "登录成功"));
    }

    /**
     * 认证失败
     *
     * @param request
     * @param response
     * @param failed
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        DataToHttpServletResponseUtil.msgToResponseWhenSuccess(response, JsonResponseUtil.fail(ResponseCodeAndMsgEnum.FAILE.getCode(), "用户名或密码错误"));
    }
}

 

token检测过滤器:

public class TokenVerifyFilter extends BasicAuthenticationFilter {

    private RsaProperties rsaProperties;

    public TokenVerifyFilter(AuthenticationManager authenticationManager, RsaProperties rsaProperties) {
        super(authenticationManager);
        this.rsaProperties = rsaProperties;
    }

    /**
     * 过滤请求
     *
     * @param request
     * @param response
     * @param chain
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain chain) {
        try {
            //请求体的头中是否包含Authorization
            String header = request.getHeader("Authorization");
            //Authorization中是否包含Bearer,不包含直接返回
            if (header == null || !header.startsWith("Bearer ")) {
                chain.doFilter(request, response);
                DataToHttpServletResponseUtil.msgToResponseWhenSuccess(response, JsonResponseUtil.fail(ResponseCodeAndMsgEnum.FAILE.getCode(), "请登录!"));
                return;
            }
            //获取权限失败,会抛出异常
            UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
            //获取后,将Authentication写入SecurityContextHolder中供后序使用
            SecurityContextHolder.getContext().setAuthentication(authentication);
            chain.doFilter(request, response);
        } catch (Exception e) {
            DataToHttpServletResponseUtil.msgToResponseWhenSuccess(response, JsonResponseUtil.fail(ResponseCodeAndMsgEnum.FAILE.getCode(), "请登录!"));
            e.printStackTrace();
        }
    }

    /**
     * 通过token,获取用户信息
     *
     * @param request
     * @return
     */
    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        String token = request.getHeader("Authorization");
        if (token != null) {
            //通过token解析出载荷信息
            Payload<UserPo> payload = JwtUtil.getInfoFromToken(token.replace("Bearer ", ""),
                    rsaProperties.getPublicKey(), UserPo.class);
            UserPo user = payload.getUserInfo();
            //不为null,返回
            if (user != null) {
                return new UsernamePasswordAuthenticationToken(user, null, user.getRoleList());
            }
            return null;
        }
        return null;
    }
}

 

编写controller的测试请求:

@RestController
@RequestMapping("/orderTest")
public class OrderController {
    //拥有ROLE_ORDER角色才能访问
    @PreAuthorize(value = "hasAnyRole('ROLE_ORDER')")
    @PostMapping("/list")
    public String list() {
        return "订单列表查询成功";
    }
}

 

@RestController
@RequestMapping("/productTest")
public class ProductController {

//拥有ROLE_PRODUCT角色才能访问
    @PreAuthorize(value = "hasAnyRole('ROLE_PRODUCT')")
    @GetMapping("/list")
    public String list(){
        return "产品列表访问成功";
    }

//登录即可访问

   @GetMapping("/detail")
   public String detail(){
    return "产品详情访问成功";
  }
}

 

测试

我们使用postman进行测试,启动项目后调用login请求(login请求是security自带的,不需要我们编写)登录,登录成功后在响应的header中获取token。如图:

b2d09c6a-cb02-4dd2-9eeb-e2b9213e13cc


访问其他需要认证的接口时需要将token放入请求头中。如图:

33c29d55-7888-4f4e-9b53-823c0d3f3095

如果登录用户没有该接口的访问权限,程序将出现异常,提示你不允许访问。而像访问/productTest/detail这样的接口就能正常访问。

 

本文主要在于演示security如何实现认证授权,如果对文中代码有疑问或者有什么建议的同学欢迎留言或添加好友交流。


如果你喜欢我的内容,就请打赏一下吧
微信
支付宝
温馨提示: 你的打赏金额会直接转入对方账户,不可退回。

评论专区


审核通过的评论(0)
暂无评论信息
个人名片

  欢迎来到“浩瀚星尘”的个人博客!
  首先,该博客用于分享本人的生活事迹与兴趣爱好; 此外,该博客的主要作用便是与广大的小伙伴一起分享探讨开发技术, 希望大家多多关照。

网名: 浩瀚星尘
城市: 重庆
工作: java