Spring Security 结合 Jwt 实现无状态登录

首先

什么是有状态

什么是无状态

江南一点雨

如何实现无状态

无状态登录的流程:

  • 首先客户端发送账户名/密码到服务端进行认证
  • 认证通过后,服务端将用户信息加密并且编码成一个 token,返回给客户端
  • 以后客户端每次发送请求,都需要携带认证的 token
  • 服务端对客户端发送来的 token 进行解密,判断是否有效,并且获取用户登录信息

JWT实例

一,Spring Security 配置

/**
 * @program: security01
 * @author: Mr-Jies
 * 
 **/
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

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

    //授权
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/hello").hasRole("user")
                .antMatchers("/admin").hasRole("admin")
                .anyRequest().authenticated()
                .and()
                .addFilterBefore(new JwtLoginFilter("/login",authenticationManager()), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(new JwtFilter(),UsernamePasswordAuthenticationFilter.class)
                .csrf().disable();
    }

    //认证
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("admin")
                .password(new BCryptPasswordEncoder().encode("123"))
                .roles("admin")
                .and()
                .withUser("jie")
                .password(new BCryptPasswordEncoder().encode("jie"))
                .roles("user");
    }
}

二,JWT 过滤器配置

1,用户登录的过滤器
/**
 * @program: security01
 * @author: Mr-Jies
  用户登录的过滤器,在用户的登录的过滤器中校验用户是否登录成功,
 *          如果登录成功,则生成一个token返回给客户端,登录失败则给前端一个登录失败的提示。
 * 
 **/

public class JwtLoginFilter extends AbstractAuthenticationProcessingFilter {


    public JwtLoginFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager) {
        super(defaultFilterProcessesUrl);
        setAuthenticationManager(authenticationManager);
    }

    //自动校验
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        User user = new ObjectMapper().readValue(request.getInputStream(), User.class);
//        从登录参数中提取出用户名密码,然后调用AuthenticationManager.authenticate()方法去进行自动校验。
        return getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()));
    }

    //校验成功
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                            FilterChain chain, Authentication authResult) throws IOException, ServletException {

        Collection<? extends GrantedAuthority> authorities = authResult.getAuthorities();
        StringBuffer as = new StringBuffer();
        //将用户角色遍历然后用一个 , 连接起来,
        for (GrantedAuthority authority : authorities) {
            as.append(authority.getAuthority()).append(",");
        }
        // 然后再利用Jwts去生成token,按照代码的顺序,生成过程一共配置了四个参数,
        //分别是用户角色、主题、过期时间以及加密算法和密钥,
        String jwt = Jwts.builder()
                .claim("authorities",as)
                .setSubject(authResult.getName())
                .setExpiration(new Date(System.currentTimeMillis()+10*60*1000))
                .signWith(SignatureAlgorithm.HS512,"wuweijie")
                .compact();
        // 然后将生成的token写出到客户端
        response.setContentType("application/json;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.write(new ObjectMapper().writeValueAsString(jwt));
        writer.flush();
        writer.close();
    }

    //校验失败
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        response.setContentType("application/json;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.write("登录失败!");
        writer.flush();
        writer.close();
    }
}
2,校验token的过滤器
/**
 * @program: security01
 * @author: Mr-Jies
 * 
 * 当其他请求发送来,校验token的过滤器,如果校验成功,就让请求继续执行
 **/

public class JwtFilter extends GenericFilterBean {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        //首先从请求头中提取出 authorization 字段,这个字段对应的value就是用户的token。
        String jwtToken = request.getHeader("authorization");
        System.out.println("authorities:"+jwtToken);
        //将提取出来的token字符串转换为一个Claims对象,
        Claims claims = Jwts.parser()
                .setSigningKey("wuweijie")
                .parseClaimsJws(jwtToken.replace("Bearer",""))
                .getBody();
        //再从Claims对象中提取出当前用户名和用户角色,
        String username = claims.getSubject();
        List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList((String) claims.get("authorities"));
        //创建一个UsernamePasswordAuthenticationToken放到当前的Context中,
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, null, authorities);
        SecurityContextHolder.getContext().setAuthentication(token);
        //然后执行过滤链使请求继续执行下去
        filterChain.doFilter(request,servletResponse);

    }
}

测试

控制层

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "hello jwt !";
    }
    @GetMapping("/admin")
    public String admin() {
        return "hello admin !";
    }
}

开始~

在这里插入图片描述
将服务器返回的token数据加入到 hello的请求中 就可以顺利访问 hello 了
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值