美文网首页security
spring-security总结

spring-security总结

作者: 三不猴子 | 来源:发表于2018-01-20 09:41 被阅读66次

    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<? extends GrantedAuthority> 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());
    
    - 总结通过这些可以进行认证和授权,如果不能通过的操作都会被屏蔽,所以能发其请求的都是验证通过的。

    相关文章

      网友评论

      • Janicerant:6666
        Janicerant:@南有乔木123 可以的,真的不错
        三不猴子:@冰糖雪九月你好 哈哈,公司大佬在给我讲东西的时候我心里一直喊666😂😂😂代码确实写的很好

      本文标题:spring-security总结

      本文链接:https://www.haomeiwen.com/subject/nxwzoxtx.html