-
springboot web项目都能正常跳转(引入security启动依赖前)
-
引入spring-boot-starter-security,
-
全都访问不了了,包括home.html静态资源也访问不了,全部都重定向到http://localhost:8080/login security自己默认的登录页;
-
输入登录名和密码: user(默认) 密码(会输出在控制台),登录成功
-
UserDetailsService 接口
- 唯一方法: 根据用户名, 返回UserDetails用户信息对象
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException - 实现逻辑: 根据用户名到数据库去查询用户,
如果没查到,应该手动抛出UsernameNotFoundException;
如果查到了,应该比对用户传过来的密码和数据库的密码
- 唯一方法: 根据用户名, 返回UserDetails用户信息对象
-
UserDetails 接口
-
String getUsername();
-
String getPassword();
-
Collection<? extends GrantedAuthority> getAuthorities();
-
boolean isAccountNonExpired();
-
boolean isAccountNonLocked();
-
boolean isCredentialsNonExpired();
-
boolean isEnabled();
-
security框架提供了实现类 User
public User(String username,
String password,
boolean enabled,
boolean accountNonExpired,
boolean credentialsNonExpired,
boolean accountNonLocked,
Collection<? extends GrantedAuthority> authorities)
-
-
PasswordEncoder 密码解析器
-
String encode(CharSequence rawPassword); // 加密密码
-
boolean matches(CharSequence rawPassword, String encodedPassword); // 匹配密码
-
BCryptPasswordEncoder 接口实现(推荐)
- 加密/匹配密码 生成盐逻辑 参考: https://www.cnblogs.com/yangzhixue/p/12257885.html
-
-
-
自定义登录逻辑
- 注入passwordEncoder,security就不会在控制台打印密码了
- 在security的登录页输入用户名、密码,security将会自己调用UserDetailsService实现类对象的loadUserByUsername方法,
返回UserDetails对象,然后做密码匹配,决定是否登录成功 - (这里的是用的security自己的登录页面)
-
自定义登录页面和自定义登录成功或失败的处理逻辑
-
继承WebSecurityConfigurerAdapter,重写configure(HttpSecurity http)方法,
-
在此方法中配置自定义登录页面、自定义登录请求、放行登录请求、(以及关闭csrf否则走不了登录逻辑)、
自定义登录表单提交用户名和密码、登录失败跳转、登录成功跳转 -
UsernamePasswordAuthenticationFilter 过滤器
-
默认的表单提交名称: username & password
-
限定了 /login post请求方式
-
可配置,因为这个authFilter被FormLoginConfigurer所持有,可以设置这个过滤器
-
-
AuthenticationSuccessHandler 登录成功之后,自定义逻辑
-
该接口中唯一方法: void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException;- Authentication 该方法回传了认证对象
-
该接口中定义的方法
- Collection<? extends GrantedAuthority> getAuthorities();
- Object getCredentials();
- Object getDetails();
- Object getPrincipal();
- boolean isAuthenticated();
- void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
-
UsernamePasswordAuthenticationToken 作为Authentication的实现
- User对象(UserDetailsService通过loadByUsername返回的UserDetails接口实现类对象)
- authorities属性(list集合)
- 其中元素为 SimpleGrantedAuthority
- WebAuthenticationDetails对象
- remoteAddress属性
- sessionId属性
- authenticated
- 登陆成功,为true
-
- Authentication 该方法回传了认证对象
-
-
AuthenticationFailureHandler 登录失败之后,自定义逻辑
- 该接口中唯一方法: void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException;- AuthenticationException 回传认证异常
- 该接口中唯一方法: void onAuthenticationFailure(HttpServletRequest request,
-
-
-
访问控制
-
公式:[url匹配规则.权限控制方法]
- 注意配置顺序有影响
-
url匹配规则
-
.anyRequest().authenticated() 访问所有资源都需要授权
- anyRequest 匹配所有请求
-
antMatcher
-
antMatchers(String… antPatterns)
- 每个参数都是一个ant表达式,用于匹配url规则
- ant匹配规则如下
- ?: 匹配一个字符
- *: 匹配0个或多个字符
- **: 匹配0个或多个目录
- 示例:
- antMatchers(“/js/“,”/css/”).permitAll()
- 放行js和css文件夹上下的所有资源
- antMatchers(“/**/*.js”).permitAll()
- 放行所有js文件
- antMatchers(“/js/“,”/css/”).permitAll()
-
antMatchers(HttpMethod method, String… antPatterns)
-
-
regexMatchers
-
regexMatchers(String… regexPatterns)
- 参数是正则表达式
- 示例:
- regexMatchers(“.+[.]js”).permitAll()
-
regexMatchers(HttpMethod method, String… antPatterns)
-
-
mvcMatchers
.mvcMatchers(“img/**”).servletPath(“/v1”).permitAll
-
-
权限控制方法
-
permitAll()
-
denyAll()
-
anonymous()
-
rememberMe()
-
authenticated()
-
fullyAuthenticated()
- rememberMe无效,需要登录
-
hasRole(String role)
- 这里的role参数不能以ROLE_打头,严格区分大小写;
- 创建UserDetails对象的role角色时,要以Role_打头的字符串
-
hasAnyRole(String… roles)
-
hasAuthority(String authority)
-
hasAnyAuthority(String… authorities)
-
hasIpAddress(String ipaddressExpression)
-
-
自定义因权限不足而拒绝访问时的处理逻辑
- AccessDeniedHandler
- 唯一方法: void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException,
ServletException;
- 唯一方法: void handle(HttpServletRequest request, HttpServletResponse response,
- AccessDeniedHandler
-
access自定义权限控制访问
-
上面的权限控制都是基于access表达式
- 如:
- access(‘permitAll’) <==> .permitAll() [可以点进入方法,就可以看到]
- access(“hasRole('ROLE_” + role + “')”) <==> .hasRole(String role) [从这里就可以看出为什么返回的User的authority要加‘ROLE_’前缀的原因了]
- 如:
-
自定义access权限控制方法
- .antMatchers(“/access.html”).access(“@myAccessImpl.hasPermission(request,authentication)”)
- 可以思考下,security已内置的实现,比如hasRole
- 其实就是访问什么样的资源,需要什么样的权限,这个权限从userDetails对象中获取grantedAuthorities进行判断
-
-
6.基于注解访问控制
- 使用步骤
- 注解默认不可用,需要通过@EnableGlobalMethodSecurity开启后,并且启用对应的注解(比如securedEnabled=true),才能使用
- 如果设置的条件允许,程序正常执行。如果不允许,将会抛出异常AccessDeniedException
- 注解可以写到Service接口或方法上,也可以写道Controller或Controller方法上
- 注解
- @Secured
- 专门用于判断是否具有角色的,能写在类或方法上。参数要以ROLE_打头
- @PreAuthorize/@PostAuthorize
- 都是基于方法或类级别的注解
- @PreAuthorize 表示访问方法或类在执行之前先判断权限。
- 注解的参数和access()方法参数取值相同,都是权限表达式
- 可以写ROLE_打头,也可以不以ROLE_打头
- @PostAuthorize 表示访问方法或类在执行结束后判断权限
- 很少使用
- 再来补充一点
- 经过测试,在构造UserDetails对象时,传入给GrantedAuthority的字符串,
如果是以ROLE_打头的,那么这是个角色权限,否则是资源权限
7.记住我功能
- 使用步骤:
- 只需要在登录时,添加一个rememberMe的参数为true,spring security就会自动把用户存到数据源中,以后就可以不登陆进行访问了。默认2周
- 在security配置类中配置记住我的功能
8.thymeleaf模板权限控制
- 使用步骤:
- 导入thymeleaf对security的支持依赖
- (版本需要对应,否则无效且无报错,
比如这个项目导入的就是thymeleaf-extras-springsecurity4
而不是thymeleaf-extras-springsecurity5)
- 使用标签控制模板显示/隐藏
- 退出登录
-
自定义退出登录url,以及退出登陆后重定向到的url(需要放行)
-
LogoutConfigurer
-
默认添加了SecurityContextLogoutHandler,它是一个LogoutHandler
- 它会销毁session会话对象
- 它会销毁authentication认证对象
-
可以自定义添加LogoutHandler
- 通过 SecurityContextHolder 的静态方法获取当前登录用户认证对象
-
也可以自定添加LogoutSuccessHandler
-
退出登录,会把rememberMe存储的数据也给删掉
-
-
10.csrf
- Cross-site request forgery 跨站请求伪造。通过伪造用户请求访问受信任站点的非法请求访问。
- 跨域: 只要网络协议、ip地址、端口中任何一个不相同就是跨域请求
- 从spring security4开始CSRF防护默认开启。默认会拦截请求,进行csrf处理。csrf为了保证不是其它第3放网站访问,
要求访问时须携带参数名为_csrf值为token的内容(token由后端产生),如果token和服务端token匹配,则正常访问。
- 不关闭csrf,虽然可以正常登录,但是其它请求好像都有问题了(后面再看下什么原因)
- 应该是其它请求没有携带这个csrf的token
- 参考: https://blog.youkuaiyun.com/hanxiaotongtong/article/details/103451936
-
Oauth2
-
Oauth是一个关于授权(authorization)的开放网络标准
- 参考: http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
-
授权模式
- 参考: https://blog.youkuaiyun.com/bylfsj/article/details/102863842
- 授权码模式(authorization code)
- 授权码模式 是功能最完整,流程最严密 的授权模式
- 简化模式(implicit)
- 密码模式(resource owner password credentials)
- 比较重要
- 客户端模式(client credentials)
-
-
spring security Oauth2架构
- 参考: https://blog.youkuaiyun.com/tuyong1972873004/article/details/107257780
- https://blog.youkuaiyun.com/lwd2307997664/article/details/115322334
-
jwt
- 由3部分组成: header.payload.signature
-
header包含了所使用的算法,
- 是直接对头部json字符串进行一个Base64编码之后的一个字符串
-
payload包含了负载,可以自定义声明(claim),
- 是直接对负载json字符串进行一个Base64编码之后的一个字符串
-
signature
- 使用header编码之后的字符串 + payload编码之后的字符串 + 一个密钥 使用header指定的算法 生成
-
最后使用.将以上3部分组合起来
- header.payload.signature
-
- 由3部分组成: header.payload.signature