三种配置方法

configure(WebSecurity) 通过重载,配置Spring Security的Filter链
configure(HttpSecurity) 通过重载,配置如何通过拦截器保护请求,用来拦截请求,配置白名单和过滤
configure(AuthenticationManagerBuilder) 通过重载,配置user-detail服务,身份认证接口调用

用户存储方式

基于内存
auth.inMemoryAuthentication()
.withUser(“admin”).password(“admin”).roles(“ADMIN”,“USER”)
.and()
.withUser(“user”).password(“user”).roles(“USER”);
roles()是authorities()方法的简写
roles()自带"ROLE_"前缀,.authorities(“ROLE_ADMIN”,“ROLE_USER”)

其他方法
accountExpired(boolean) 定义账号是否已经过期
accountLocked(boolean) 定义账号是否已经锁定
and() 用来连接配置
authorities(GrantedAuthority…) 授予某个用户一项或多项权限
authorities(List<? extends GrantedAuthority>) 授予某个用户一项或多项权限
authorities(String…) 授予某个用户一项或多项权限
credentialsExpired(boolean) 定义凭证是否已经过期
disabled(boolean) 定义账号是否已被禁用
password(String) 定义用户的密码
roles(String…) 授予某个用户一项或多项角色

xml方法
security:authentication-manager
security:authentication-provider
security:user-service
<security:user name=“admin” authorities=“ROLE_ADMIN” password=“admin”/>
<security:user name=“user” authorities=“ROLE_USER” password=“user”/>
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>

基于db
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery(“select account,password,true from user where account=?”)
.authoritiesByUsernameQuery(“select account,role from user where account=?”);

基于自定义用户认证
使用UserDetailService接口
auth.userDetailsService(userDetailsService)
封装密码形式,使用security自带类BCryptPasswordEncoder
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
bCryptPasswordEncoder申明为一个bean

密码加密策略

NoOpPasswordEncoder 明文方式保存
BCtPasswordEncoder 强hash方式加密(若依使用该方法加密)
StandardPasswordEncoder SHA-256方式加密
实现PasswordEncoder接口 自定义加密方式

HttpSecurity

httpSecurity
// CSRF禁用,因为不使用session
.csrf().disable()
// 认证失败处理类
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// 基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// 过滤请求
.authorizeRequests()
// 对于登录login 验证码captchaImage 允许匿名访问
.antMatchers("/ssologin/token", “/ssologin”,"/login", “/captchaImage”,"/getssourl","/ssologin").anonymous()
.antMatchers(
HttpMethod.GET,
“/.html",
"/**/
.html”,
“//*.css",
"/
/*.js”
).permitAll()
.antMatchers("/processDefinition/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated()
.and()
.headers().frameOptions().disable();
httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
// 添加JWT filter
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
// 添加CORS filter
httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
其他方法
access(String) 如果给定的SpEL表达式计算结果为true,就允许访问
anonymous() 允许匿名用户访问
authenticated() 允许认证过的用户访问
denyAll() 无条件拒绝所有访问
fullyAuthenticated() 如果用户是完整认证的话(不是通过Remember-me功能认证的),就允许访问
hasAnyAuthority(String…) 如果用户具备给定权限中的某一个的话,就允许访问
hasAnyRole(String…) 如果用户具备给定角色中的某一个的话,就允许访问
hasAuthority(String) 如果用户具备给定权限的话,就允许访问
hasIpAddress(String) 如果请求来自给定IP地址的话,就允许访问
hasRole(String) 如果用户具备给定角色的话,就允许访问
not() 对其他访问方法的结果求反
permitAll() 无条件允许访问
rememberMe() 如果用户是通过Remember-me功能认证的,就允许访问

强制安全性通道
.requiresChannel()方法通过https加密发生
防止CSRF
默认开启,关闭.csrf().disable()
记住登录
设置cookie和过期时间等
.rememberMe().key(“abc”).rememberMeParameter(“remember_me”).rememberMeCookieName(“my-remember-me”).tokenValiditySeconds(86400);

认证流程

用户使用用户名和密码登录
用户名密码被过滤器(默认为 UsernamePasswordAuthenticationFilter)获取到,封装成 Authentication
token(Authentication实现类)传递给 AuthenticationManager 进行认证
AuthenticationManager 认证成功后返回一个封装了用户权限信息的 Authentication 对象
通过调用 SecurityContextHolder.getContext().setAuthentication(…) 将 Authentication 对象赋给当前的 SecurityContext

用户登录的代码调用流程
输入用户名和密码和验证码,调用login接口,将验证码通过uuid生成后存在缓存与用户输入做比较,成功通过Authentication类进行验证
通过AuthenticationManager接口和UsernamePasswordAuthenticationToken构造类

/1/ 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
authentication = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(username, password));
loadUserByUsername方法进行判断,抛出不同登录失败异常,并且创建登录用户userdetails,在SimpleGrantedAuthority类中放入GROUP_(用来区分该user的post)和ROLE_ACTIVITI_USER(用来验证activiti7新api)流
最后返回loginuser构造方法,分别放入user类,权限集合类和验证集合类
user类中通过SimpleGrantedAuthority获取权限方法
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}

/2/通过执行手写的AsyncManager和AsyncFactory类方法将用户名和状态写入,将用户类放去处理token
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message(“user.login.success”)));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();

/3/ 生成token字符串
tokenService.createToken(loginUser);

其他

相关类
WebSecurityConfigurerAdapter
为创建 WebSecurityConfigurer 实例提供方便的基类,重写它的 configure 方法来设置安全细节
configure(HttpSecurity http):重写该方法,通过 http 对象的 authorizeRequests()方法定义URL访问权限,默认会为 formLogin() 提供一个简单的测试HTML页面
_configureGlobal(AuthenticationManagerBuilder auth) _:通过 auth 对象的方法添加身份验证
SecurityContextHolder
SecurityContextHolder 用于存储安全上下文信息(如操作用户是谁、用户是否被认证、用户权限有哪些),它用 ThreadLocal 来保存 SecurityContext,者意味着 Spring Security 在用户登录时自动绑定到当前现场,用户退出时,自动清除当前线程认证信息,SecurityContext 中含有正在访问系统用户的详细信息
AuthenticationManagerBuilder
用于构建认证 AuthenticationManager 认证,允许快速构建内存认证、LDAP身份认证、JDBC身份验证,添加 userDetailsService(获取认证信息数据) 和 AuthenticationProvider’s(定义认证方式)
UserDetailsService
该接口仅有一个方法 loadUserByUsername,Spring Security 通过该方法获取
UserDetails
我们可以实现该接口来定义自己认证用户的获取方式(如数据库中获取),认证成功后会将 UserDetails 赋给 Authentication 的 principal 属性,然后再把 Authentication 保存到 SecurityContext 中,之后需要实用用户信息时通过 SecurityContextHolder 获取存放在 SecurityContext 中的 Authentication 的 principal
Authentication
Authentication 是一个接口,用来表示用户认证信息,在用户登录认证之前相关信息(用户传过来的用户名密码)会封装为一个 Authentication 具体实现类对象,默认情况下为 UsernamePasswordAuthenticationToken,登录之后(通过AuthenticationManager认证)会生成一个更详细的、包含权限的对象,然后把它保存在权限线程本地的 SecurityContext 中,供后续权限鉴定用
GrantedAuthority
GrantedAuthority 是一个接口,它定义了一个 getAuthorities() 方法返回当前 Authentication 对象的拥有权限字符串,用户有权限是一个 GrantedAuthority 数组,每一个 GrantedAuthority 对象代表一种用户
通常搭配 SimpleGrantedAuthority 类使用
AuthenticationManager
AuthenticationManager 是用来处理认证请求的接口,它只有一个方法 authenticate(),该方法接收一个 Authentication 作为对象,如果认证成功则返回一个封装了当前用户权限信息的 Authentication 对象进行返回
它默认的实现是 ProviderManager,但它不处理认证请求,而是将委托给 AuthenticationProvider 列表,然后依次使用 AuthenticationProvider 进行认证,如果有一个 AuthenticationProvider 认证的结果不为null,则表示成功(否则失败,抛出 ProviderNotFoundException),之后不在进行其它 AuthenticationProvider 认证,并作为结果保存在 ProviderManager
认证校验时最常用的方式就是通过用户名加载 UserDetails,然后比较 UserDetails 密码与请求认证是否一致,一致则通过,Security 内部的 DaoAuthenticationProvider 就是实用这种方式
认证成功后加载 UserDetails 来封装要返回的 Authentication 对象,加载的 UserDetails 对象是包含用户权限等信息的。认证成功返回的 Authentication 对象将会保存在当前的 SecurityContext 中
AuthenticationProvide
AuthenticationProvider 是一个身份认证接口,实现该接口来定制自己的认证方式,可通过 UserDetailsSevice 对获取数据库中的数据
该接口中有两个需要实现的方法:
Authentication authenticate(Authentication authentication):认证处理,返回一个 Authentication 的实现类则代表成功,返回 null 则为认证失败
supports(Class<?> aClass):如果该 AuthenticationProvider 支持传入的 Authentication 认证对象,则返回 true ,如:return aClass.equals(UsernamePasswordAuthenticationToken.class);
AuthorityUtils
是一个工具包,用于操作 GrantedAuthority 集合的实用方法:
commaSeparatedStringToAuthorityList(String authorityString):从逗号分隔符中创建 GrantedAuthority 对象数组
AbstractAuthenticationProcessingFilter
该抽象类继承了 GenericFilterBean,是处理 form 登录的过滤器,与 form 登录相关的所有操作都在该抽象类的子类中进行(UsernamePasswordAuthenticationFilter 为其子类),比如获取表单中的用户名、密码,然后进行认证等操作
该类在 doFilter 方法中通过 attemptAuthentication() 方法进行用户信息逻辑认证,认证成功会将用户信息设置到 Session 中
UserDetails
代表了Spring Security的用户实体类,带有用户名、密码、权限特性等性质,可以自己实现该接口,供 Spring Security 安全认证使用,Spring Security 默认使用的是内置的 User 类
将从数据库获取的 User 对象传入实现该接口的类,并获取 User 对象的值来让类实现该接口的方法
通过 Authentication.getPrincipal() 的返回类型是 Object,但很多情况下其返回的其实是一个 UserDetails 的实例

注解与Spring EL

@EnableWebSecurity:开启 Spring Security 注解

@EnableGlobalMethodSecurity(prePostEnabled=true):开启security方法注解

@PreAuthorize:在方法调用前,通过SpringEL表达式限制方法访问

@PostAuthorize:允许方法调用,但时如果表达式结果为false抛出异常

@PostFilter:允许方法调用,但必须按表达式过滤方法结果

@PreFilter:允许方法调用,但必须在进入方法前过滤输入值

spring cloud gateway 接口白名单 spring security 白名单规则_ide

过滤器链
SecurityContextPersistenceFilter
过滤器链头,是从 SecurityContextRepository 中取出用户认证信息,默认实现为 HttpSessionSecurityContextRepository,它会从 Session 中取出已认证的用户信息,提高效率,避免每次请求都要查询用户认证信息
取出之后会放入 SecurityContextHolder 中,以便其它 filter 使用,SecurityContextHolder 使用 ThreadLocal 存储用户认证信息,保证线程之间信息隔离,最后再 finally 中清除该信息
WebAsyncManagerIntegrationFilter
提供了对 SecurityContext 和 WebAsyncManager 的集成,会把 SecurityContext 设置到异步线程,使其也能获取到用户上下文认证信息
HanderWriterFilter
会往请求的 Header 中添加相应的信息
CsrfFilter
跨域请求伪造过滤器,通过客户端穿来的 token 与服务端存储的 token 进行对比来判断请求
LogoutFilter
匹配URL,默认为 /logout,匹配成功后则会用户退出,清除认证信息,若有自己的退出逻辑,该过滤器可以关闭
UsernamePasswordAuthenticationFilter
登录认证过滤器,默认是对 /login 的 POST 请求进行认证,首先该方法会调用 attemptAuthentication 尝试认证获取一个 Authentication 认证对象,然后通过 sessionStrategy.onAuthentication 执行持久化,也就是保存认证信息,然后转向下一个 Filter,最后调用 successfulAuthentication 执行认证后事件
attemptAuthentication 该方法是认证的主要方法,认证基本流程为 UserDeatilService 根据用户名获取到用户信息,然后通过 UserDetailsChecker.check 对用户状态进行校验,最后通过 additionalAuthenticationChecks 方法对用户密码进行校验完后认证后,返回一个认证对象
DefaultLoginPageGeneratingFilter
当请求为登录请求时,生成简单的登录页面,可以关闭
BasicAuthenticationFilter
Http Basci 认证的支持,该认证会把用户名密码使用 base64 编码后放入 header 中传输,认证成功后会把用户信息放入 SecurityContextHolder 中
RequestCacheAwareFilter
恢复被打断时的请求
SecurityContextHolderAwareRequestFilter
针对 Servlet api 不同版本做一些包装
AnonymousAuthenticationFIlter
SecurityContextHolder 中认证信息为空,则会创建一个匿名用户到 SecurityContextHolder 中
SessionManagementFilter
与登录认证拦截时作用一样,持久化用户登录信息,可以保存到 Session 中,也可以保存到 cookie 或 redis 中
ExceptionTranslationFilter
异常拦截,处于 Filter 链条后部,只能拦截其后面的节点并着重处理 AuthenticationException 与 AccessDeniedException 异常