# security_demo **Repository Path**: WHQ95/security_demo ## Basic Information - **Project Name**: security_demo - **Description**: security的一个demo和自己的理解 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-09-03 - **Last Updated**: 2021-11-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README security-demo security主要分为两个部分认证和授权 认证流程:1.根据WebSecurityConfigurerAdapter的配置判断是否需要拦截 2.如果接口符合UsernamePasswordAuthenticationFilter拦截规则会默认封装为UsernamePasswordAuthenticationToken(这里有一个暗坑账号密码的取值是从request里面去的是formdata的形式,如果用json会取不到只,要用json的话需要重写一下这个取值方式)去调用AuthenticationManager.authenticate()方法除了满足这个过滤的其他都不会主动去调用authenticate认证方法 3.authenticate的认证流程:AuthenticationManager有很多个实现类,默认调用其中一个实现类ProviderManager类 4.ProviderManager重写AuthenticationManager的authenticate()方法,内部有一个认证链其中有会用AbstractUserDetailsAuthenticationProvider去做认证 5.AbstractUserDetailsAuthenticationProvider.authenticate()内的retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication)是DaoAuthenticationProvider.retrieveUser()方法 6.DaoAuthenticationProvider.retrieveUser()方法里面会用的UserDetailsService.loadUserByUsername()方法获取UserDetails,这个UserDetails是用来后面和用户传进来的账号密码做比较用的,所以如果要用数据库的账号密码需要继承UserDetailsService重写loadUserByUsername() 7.获取到UserDetails后AbstractUserDetailsAuthenticationProvider.authenticate()会继续执行preAuthenticationChecks.check(user)方法去判断获取到的这个UserDeatails是否禁用,是否有效,是否过期。。。 8.校验通过开始比较密码additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication)是DaoAuthenticationProvider.additionalAuthenticationChecks()方法;里面会用入参UsernamePasswordAuthenticationToken和Userdetails的密码做比较,如果需要自定义密码校验规则可以继承DaoAuthenticationProvider类重写additionalAuthenticationChecks方法 9.密码比对成功后会把Userdetails的值覆盖入参的UsernamePasswordAuthenticationToken并做返回 授权:暂时没写 相关接口: WebSecurityConfigurerAdapter配置接口:做一些拦截配置和定义一些securityd的基本属性 AuthenticationSuccessHandler认证成功接口:认证成功会经过这个接口的onAuthenticationSuccess方法,需要自定义可以继承重写 AuthenticationFailureHandler认证失败接口:认证失败会经过这个接口的onAuthenticationFailure方法,需要自定义可以继承重写 AuthenticationEntryPoint认证异常接口:认证异常会经过这个接口的commence方法里面捕获了所有认证失败的异常可以捕获特定返回,需要自定义可以继承重写 AccessDeniedHandler授权失败皆苦:授权失败会经过这个接口的handle方法,需要自定义可以继承重写 注(所有自定义的类都需要加@Component注解交由spring管理) 关于security+jwt遇到的坑 1.jwt生成token的接口是不需要拦截的,但是又需要验证账号密码所以需要用到AuthenticationManager.authenticate()方法,但是AuthenticationManager没发注入所以这个时候需要去实现了WebSecurityConfigurerAdapter的类注入一个Authentication类给spring管理 @Bean(name = BeanIds.AUTHENTICATION_MANAGER) @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } 2.WebSecurityConfigurerAdapter配置接口的configure(AuthenticationManagerBuilder auth)方法里面的.passwordEncoder(bcryptPasswordEncoder())其实是在给DaoAuthenticationProvider设置密码加密方式这样是为了后面做认证密码比较 userDetailService是自己自定义的一个实现了UserDetailsService接口的类 @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailService) .passwordEncoder(bcryptPasswordEncoder()); } 3.自己写一个jwt的过滤器继承OncePerRequestFilter类里面做一下token的获取和验证,验证通过返回 UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username,null,null); JwtAuthUser jwtAuthUser = new JwtAuthUser(user); authentication.setDetails(jwtAuthUser); SecurityContextHolder.getContext().setAuthentication(authentication); 写这个的原因是告诉spring security当前登录的用户,这里能通过的原因是因为1.请求的接口不是UsernamePasswordAuthenticationFilter需要拦截的接口所以只要自己不去调用认证方法他就不会去认证直接就过了 没有写jwt过滤之前调用别的接口会被拦截的原因可能是因为security没有检查到SecurityContextHolder.getContext().getAuthentication(authentication)内有值。 4.生成token的接口有两种方式: 1:Api地址配置到UsernamePasswordAuthenticationFilter中去被拦截做认证,在认证成功过滤器返回token 2:不拦截生成token的api地址,自己去调用认证方法认证成功后自己返回token。