spring-security总结

spring-security总结

今天公司大佬给我讲了一下security心里一直默默的喊666,哈哈哈,赶快写个笔记别等会儿忘,闲话不多说,开始写码代码。

  1. 写好拦截配置
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private UserDetailsService userDetailsService;

    public WebSecurityConfig(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    // 设置 HTTP 验证规则
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable().authorizeRequests()
                // 允许对于网站静态资源的无授权访问
                .antMatchers(
                    HttpMethod.GET,
                    "/",
                    "/*.html",
                    "/**/*.html",
                    "/**/*.css",
                    "/**/*.js"
                 ).permitAll()
                .antMatchers(HttpMethod.GET, "/captcha/get").permitAll() //验证码
                .anyRequest().authenticated()
                .and()
                .addFilter(new JWTLoginFilter(authenticationManager()))
                .addFilter(new JWTAuthenticationFilter(authenticationManager()));
        //以下这句就可以控制单个用户只能创建一个session,也就只能在服务器登录一次
        http.sessionManagement().maximumSessions(1).expiredUrl("/login");
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 使用自定义身份验证组件
        auth.authenticationProvider(new CustomAuthenticationProvider(userDetailsService));
    }
}

配置好了拦截器security会自动拦截url为/login的操作,并进行登录操作的相关验证

  1. 书写拦截类,这个进行了身份认证
public class JWTLoginFilter extends UsernamePasswordAuthenticationFilter {

    private AuthenticationManager authenticationManager;

    public JWTLoginFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    // 接收并解析用户凭证
    @Override
    public Authentication attemptAuthentication(HttpServletRequest req,
                                                HttpServletResponse res) throws AuthenticationException {
        try {
            LoginParam user = new ObjectMapper()
                    .readValue(req.getInputStream(), LoginParam.class);
            String captcha = user.getCaptcha();
            //将验证码加入到session中
            String session_captcha = (String) req.getSession().getAttribute(Consts.IMAGE_CODE);
            if(StringUtils.isEmpty(captcha)){
                throw new YcException(RespCode.PARAM_ERROR, "验证码为空");
            }else if(!captcha.toLowerCase().equals(session_captcha.toLowerCase())){
                throw new YcException(RespCode.PARAM_ERROR, "验证码错误");
            }
            return authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(
                            user.getUsername(),
                            user.getPassword(),
                            new ArrayList<>())
            );
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    // 用户成功登录后,这个方法会被调用,我们在这个方法里生成token
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        String token = Jwts.builder()
                .setSubject(authResult.getName())
                .setExpiration(new Date(System.currentTimeMillis() + Consts.JWT_EXPIRE)) //失效时间
                .signWith(SignatureAlgorithm.HS512, Consts.SECRET)
                .compact();
        response.addHeader(Consts.AUTH_KEY, JwtUtils.getTokenHeader(token));
        String result = JSONObject.toJSONString(new ResponseInfo());
        PrintWriter writer = response.getWriter();
        writer.write(result);
    }
}
  1. 写自定义身份验证的自定义类,同时生成token
public class CustomAuthenticationProvider implements AuthenticationProvider {

    private UserDetailsService userDetailsService;

    public CustomAuthenticationProvider(UserDetailsService userDetailsService){
        this.userDetailsService = userDetailsService;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 获取认证的用户名 & 密码
        String name = authentication.getName();
        String password = authentication.getCredentials().toString();

        // 认证逻辑
        UserDetails userDetails = userDetailsService.loadUserByUsername(name);
        if(null != userDetails){
            if(MD5Util.verify(password,userDetails.getPassword())){
                // 生成令牌,返回Authentication对象上设置的授予权限是空的  因为权限是特定于应用程序的
              //在这一步可以进行数据库查询,然后将查询的权限信息加入到Authentication 中
                Authentication auth = new UsernamePasswordAuthenticationToken(name, password, Collections.emptyList());
                return auth;
            }else {
                throw new BadCredentialsException("密码错误~");
            }
        }else {
            throw new UsernameNotFoundException("用户不存在~");
        }
    }

    // 是否可以提供输入类型的认证服务
    @Override
    public boolean supports(Class authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }

}

  1. 写UserDetailsService的实现类
@Service
public class JwtUserDetailsService implements UserDetailsService {
    @Reference(version = "1.0.0", check = false, timeout = 10000)
    IUserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.findByAccount(username);
        if (user == null) {
            throw new UsernameNotFoundException(String.format("No user found with username '%s'.", username));
        } else {
  
            return new org.springframework.security.core.userdetails.User(user.getAccount(), user.getPassword(),emptyList());
        }
    }
}

这个类要实现UserDetailsService类接口,实现这个类的目的是将用户名密码交给security管理起来。

  1. 书写登录操作的实体类,注意要这个实体类要实现UserDetails接口,实现它的目的也是为了让spring-security管理起来user实体类,在JWTLoginFilter这个中
LoginParam user = new ObjectMapper().readValue(req.getInputStream(), LoginParam.class);

可以在流中读取出用户对象就是因为这个用户登录对象实现现UserDetailsService这个类被spring管理起来这个实体

@Data
public class LoginParam implements UserDetails, Serializable{
    @NotBlank(message = "username 不能为空")
    private String username;
    @NotBlank(message = "password 不能为空")
    private String password;

    private String captcha;

    @Override
    public Collection getAuthorities() {
        return null;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }


    @Override
    public String toString() {
        return this.username;
    }

    @Override
    public int hashCode() {
        return username.hashCode();
    }

    @Override
    public boolean equals(Object rhs) {
        return rhs instanceof User ? this.username.equals(((User) rhs).getUsername()) : false;
    }
}

  1. 写授权类
public class JWTAuthenticationFilter extends BasicAuthenticationFilter {


    public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String header = request.getHeader(Consts.AUTH_KEY);
        if (header == null || !header.startsWith(JwtUtils.getAuthorizationHeaderPrefix())) {
            chain.doFilter(request, response);
            return;
        }
        UsernamePasswordAuthenticationToken authenticationToken = getUsernamePasswordAuthenticationToken(header);
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        chain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken getUsernamePasswordAuthenticationToken(String token) {
        String user = Jwts.parser()
                .setSigningKey(Consts.SECRET)
                .parseClaimsJws(token.replace(JwtUtils.getAuthorizationHeaderPrefix(), ""))
                .getBody()
                .getSubject();
        if (null != user) {
            return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
        }
        return null;
    }
}

可能在这里会有疑惑,在这里的操作没有进行数据库的查询啊,框架是怎么判断用户名和密码是否配呢?因为我们在JwtUserDetailsService已经把用户名和密码交给spring来管理了。

return new org.springframework.security.core.userdetails.User(user.getAccount(), user.getPassword(),emptyList());
- 总结通过这些可以进行认证和授权,如果不能通过的操作都会被屏蔽,所以能发其请求的都是验证通过的。

你可能感兴趣的:(spring-security总结)