spring security技术分享(五)

简介: 用户认证就是判断一个用户的身份是否合法的过程,用户去访问系统资源时系统要求验证用户的身份信息,身份合法方可继续访问,不合法则拒绝访问。 认证是确认某主体在某系统中是否合法、可用的过程。这里的主体既可以是登录系统的用户,也可以是接入的设备或者其他系统。

4.4.2 会话控制


我们可以通过以下选项准确控制会话何时创建以及Spring Security如何与之交互:


机制

描述

always 如果没有session存在就创建一个

ifRequired

如果需要就仓U建一个Session (默认)登录时
never SpringSecurity将不会创建Session ,但是如果应用中其他地方创建了Session ,那么Spring Security将会使用它。
stateless

SpringSecurity将绝对不会创建Session ,也不使用Session


通过以下配置方式对该选项进行配置:


@Override
protected void configure(HttpSecurity http) throws Exception {
    http.sessionManagement()
      .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
}


默认情况下,Spring Security会为每个登录成功的用户会新建一个Session ,就是ifRequired。


若选用never,则指示Spring Security对登录成功的用户不创建Session了,但若你的应用程序在某地方新建了 session ,月B么Spring Security会用它的。


若使用stateless,则说明Spring Security对登录成功的用户不会创建Session ,你的应用程序也不会允许新建 session。并且它会暗示不使用cookie ,所以每个请求都需要重新进行身份验证。这种无状态架构适用于REST API及袞状态认证机制。


会话超时


可以在sevlet容器中设置Session的超时时间,如下设置Session有效期为3600s ; spring boot配置文件:


server.servlet.session.timeout=3600s


session超时之后,可以通过Spring Security设置跳转的路径。


http.sessionManagement()
  .expiredUrl( "/login-view?error=EXPIRED_SESSI0Nn")
  .invalidSessionUrl("/login-view?error=INVALID_SESSION");


expired指session过期,invalidSession指传入的sessionid无效。


安全会话cookie


我们可以使用httpOnly和secure标签来保护我们的会话cookie :


  • httpOnly :如果为true ,那么浏览器脚本将无法访问cookie
  • secure :如果为true,则cookie将仅通过HTTPS连接发送


spring boot配置文件:


server.servlet.session.cookie.http-only=true server.servlet.session.cookie.secure=true


4.6 退出


Spring security默认实现了logout退出,访问/logout,果然不出所料,退出功能Spring也替我们做好了。



         


点击"Log Out”退出成功。

退出后访问其它url判断是否成功退出。


这里也可以自定义退出成功的页面:


在WebSecurityConfig的protected void configure(HttpSecurity http)中配置:


.and()
  .logout()
  .logoutUrl("/logout")
  .logoutSuccessUrl("/login-view?logout");


当退出操作出发时,将发生:


  • 使HTTP Session 无效
  • 清除 SecurityContextHolder 路径转到 /login-view?logout


但是,类似于配置登录功能,咱们可以进一步自定义退出功能:


@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
//...
.and()
  .logout() (1)
  .logoutUrl("/logout") (2)
  .logoutSuccessUrl("/login-viewPlogout") (3)
  .logoutSuccessHandler(logoutSuccessHandler) (4)
  .addLogoutHandler(logoutHandler)  (5)
  .invalidateHttpSession(true); (6)
}


(1 )提供系统退出支持,使用WebSecurityConfigurerAdapter会自动被应用。


(2 )设置触发退出操作的URL (默认是/logout )。


(3 )退出之后跳转的URL,默认是/login?logout。


(4 )定制的LogoutSuccessHandler ,用于实现用户退出成功时的处理。如果指定了这个选项那么 logoutSuccessUrl()的设置会被忽略。


(5 )添加一个LogoutHandler ,用于实现用户退出时的清理工作.默认SecuritycontextLogoutHandler会被添加为—个 LogoutHandler。


(6 )指定是否在退出时让HttpSession无效。默认设置为true。 注意:如果让logout在GET请求下生效,必须关闭防止CSRF攻击csrf().disable()。如果开启了CSRF ,必须使用

post方式请求/logout


logoutHandler :


一般来说,LogoutHandler的实现类被用来执行必要的清理,因而他们不应该抛出异常。 下面是Spring Security提供的一些实现:


  • PersistentTokenBasedRememberMeServices 基于持久化token的RememberMe功能的相关清理
  • TokenBasedRememberMeService 基于token的RememberMe功能的相关清理
  • CookieClearingLogoutHandler 退出时Cookie的相关清理
  • CsrfLogoutHandler 负责在退出时移除csrfToken
  • SecurityContextLogoutHandler 退出时Securitycontext的相关清理 链式API提供了调用相应的LogoutHandler实现的快捷方式,比如deleteCookies().


4.7 授权


4.7.1 概述


授权的方式包括web授权和方法授权,web授权是通过url拦截进行授权,方法授权是通过方法拦截进行授权。他们都会调用accessDecisionManager进行授权决策,若为web授权则拦截器为FilterSecuritylnterceptor;若为方法授权则拦截器为Methodsecurityinterceptor。如果同时通过web授权和方法授权则先执行web授权,再执行方法授权。最后决策通过,则允许访问资源,否则将禁止访问。


4.7.2 准备环境


4.7.2.1 数据库环境


在t_user数据库创建如下表:

角色表:


CREATE TABLE 't_role' (
  'id' varchar(32) NOT NULL,
  'role_name' varchar(255) DEFAULT NULL,
  'description' varchar(255) DEFAULT NULL,
  'create_time' datetime DEFAULT NULL,
  'update_time' datetime DEFAULT NULL,
  'status' char(l) NOT NULL,
  PRIMARY KEY ('id'),
  UNIQUE KEY 'unique_role_name' ('role_name')
)ENGINE=InnoDB DEFAULT CHARSET=utf8
insert into 't_role'('id','role_name', 'description','create_time', ' update_time', 'status') values ('1','管理员',NULL,NULL,NULL)


用户角色关系表:


CREATE TABLE 't_user_role' (
  'userid' varchar(32) NOT NULL,
  'roleid' varchar(32) NOT NULL,
  'create_time' datetime DEFAULT NULL,
  'creator' varchar(255) DEFAULT NULL,
  PRIMARY KEY ('userid','role_id')
)ENGINE=InnoDB DEFAULT CHARSET=utf8
insert into 't_user_role'('user_id','roleid','create_time','creator') values
('1','1',NULL,NULL);

权限表:

CREATE TABLE 't_permission' (
  'id' varchar(32) NOT NULL,
  'code' varchar(32) NOT NULL COMMENT,权限标识符
  'description' varchar(64) DEFAULT NULL COMMENT '描述
  'url' varchar(128) DEFAULT NULL COMMENT '请求地址
  PRIMARY KEY ('id')
)ENGINE=InnoDB DEFAULT CHARSET=utf8
insert into 't_permission' (' id',' code','description',' url') values (' 1’,’ pl','测试资源 l','/r/rl'),('2','p3','测试资源2','/r/r2');

角色权限关系表:

CREATE TABLE 't_role_permission' (
  'roleid' varchar(32) NOT NULL,
  'permission_id' varchar(32) NOT NULL,
  PRIMARY KEY ('role_id','permissionid')
)ENGINE=InnoDB DEFAULT CHARSET=utf8
insert into 't_role_permission'(' role_id','permission_id') values ('1','1')


4.7.2.2 修改 UserDetailService


1、修改dao接口

在UserDao中添加:


〃根据用户id查询用户权限
public List<String> findPermissionsByUserId(String userld){
  String sql=...略
  List<PermissionDto> list = jdbcTemplate.query(sql,new Object[]{userid}new BeanPropertyRowMappero(PermissionDto.class));
  List<String> permissions = new ArrayList<>();
  list.iterator().forEachRemaining(c->permissions.add(c.getCode())); 
  return permissions;


2、修改UserDetailService

实现从数据库读取权限


@0verride
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //登录略
  System.out.printin("username="+username);
  //根据账号去数据库查询...
  UserDto user = userDao.getUserByUsername(username);
  if(user == null)(
  return null;
  }
  //查询用户权限
  List<String> permissions = userDao.findPermissionsByUserId(user.getId());
  Stringf] perarray = new String[permissions.size()];
  permissions.toArray(perarray);
  //创建 userDetails
  UserDetails userDetails =
  User.withUsername(user.getFullname()).password(user.getPassword()).authorities(perarray)・ build(); 
  return userDetails;
}


4.7.2 web授权


在上面例子中我们完成了认证拦截,并对/r/**下的某些资源进行简单的授权保护,但是我们想进行灵活的授权控制该怎么做呢?通过给http.authorizeRequests()添加多个子节点来定制需求 ,如下代码:


@Override
protected void configure(HttpSecurity http) throws Exception {  
http .authorizeRequests() (1)
  .antMatchers("/r/rl").hasAuthority("pl")  (2)
  .antMatchers("/r/r2").hasAuthority("p2")  (3)
  .antMatchers("/r/rS").access("hasAuthority('p1') and hasAuthority('p2')") (4)
  .antMatchers("/r/**")]authenticated() (5)
  .anyRequest().permitAll() ⑹
  .and()
  .formLogin()
}   


(1 ) http.authorizeRequests()方法有多个子节点,每个macher按照他们的声明顺扇丸行。

(2)指定"/r/r1 “URL,拥有p1权限能够访问

(3 )指定”/r/r2"URL ,拥有p2权限能够访问

(4 )指定了”/r/r3”URL ,同时拥有p1和p2权限才能够访问

(5 )指定了除了r1、r2、r3之外’7r/**’'资源,同时通过身份认证就能够访问,这里使用SpEL ( Spring Expression Language )表达式。

(6 )剩余的尚未匹配的资源,不做保护。


注意: 规则的顺序是重要的,更具体的规则应该先写.现在以/admin开始的所有内容都需要具有ADMIN角色的身份验证用 户,即使是/

admin / login路径(因为/ admin / login已经被/ admin / **规则匹配,因此第二个规则被忽略).


.antMatchers("/admin/*\*").hasRole("ADMIN")
.antMatchers("/admin/login").permitAll()


因此登录页面的规则应该在/ admin / **规则之前.例如.


.antMatchers("/admin/login").permitAll()
.antMatchers("/admin/*\*").hasRole("ADMIN")


保护URL常用的方法有:


authenticated()保护URL ,需要用户登录 permitAII()指定URL无需保护,一般应用与静态资源文件


hasRole(String role)限制单个角色访问,角色将被增加"ROLE_".所以’ADMIN"将和"ROLE_ADMIN"进行比较.


hasAuthority(String authority)限制单个权限访问


hasAnyRole(String… roles)允许多个角色访问. hasAnyAuthority(String… authorities)允许多个权限访问.


access(String attribute)该方法使用SpEL表达式,所以可以创建复杂的限制.


haslpAddress(String ipaddressExpression)限制IP地址或子网


4.7.3 方法授权


现在我们已经掌握了使用如何使用http.authorizeRequests()对web资源进行授权保护,从Spring Security2.0版本开始,它支持服务层方法的安全性的支持。


@PreAuthorize,@PostAuthorize, @Secured三类注解。

我们可以在任(可@Configuration实例上使用@EnableGlobalMethodSecurity注释来启用基于注解的安全性。


以下内容将启用Spring Security的@Secured注释。


@EnableGlobalMethodSecurity(securedEnabled = true) 
public class MethodSecurityConfig (// ...
}


然后向方法(在类或接口上)添加注解就会限制对该方法的访问。Spring Security的原生注释支持为该方法定义了 一组属性。这些将被传递给AccessDecisionManager以供它作出实际的决定:


public interface BankService { 
  @Secured()
  public Account readAccount(Long id);
  @Secured()
  public Accountf] findAccounts();
  @Secured()
  public Account post(Account account, double amount);
}


以上配置标明readAccount、findAccounts方法可匿名访问,底层使用WebExpressionVoter投票器,可从 AffirmativeBased 第23 行代码跟踪。 post方法需要有TELLER角色才能访问,底层使用RoleVoter投票器。

使用如下代码可启用prePost注解的支持


@EnableGlobalMethodSecurity(prePostEnabled = true) 
public class MethodSecurityConfig {
//...
}


相应Java代码如下:


public interface BankService {
  @PreAuthorize("isAnonymous()")
  public Account readAccount(Long id);
  @PreAuthorize("isAnonymous()")
  public Accountf] findAccounts。;
  @PreAuthorize("hasAuthority('p_transfer') and hasAuthority('p_read_account')") public Account post(Account account, double amount);
}


相关文章
|
4天前
|
安全 Java API
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)(上)
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)
22 0
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)(上)
|
1月前
|
存储 安全 Java
Spring Boot整合Spring Security--学习笔记
Spring Boot整合Spring Security--学习笔记
53 0
|
15天前
|
安全 数据安全/隐私保护
Springboot+Spring security +jwt认证+动态授权
Springboot+Spring security +jwt认证+动态授权
|
4天前
|
存储 安全 Java
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)(下)
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)
17 2
|
4天前
|
安全 Cloud Native Java
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)(上)
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)
20 2
|
4天前
|
安全 Java API
第5章 Spring Security 的高级认证技术(2024 最新版)(上)
第5章 Spring Security 的高级认证技术(2024 最新版)
29 0
|
4天前
|
存储 安全 Java
第3章 Spring Security 的用户认证机制(2024 最新版)(下)
第3章 Spring Security 的用户认证机制(2024 最新版)
28 0
|
4天前
|
存储 安全 Java
第2章 Spring Security 的环境设置与基础配置(2024 最新版)(下)
第2章 Spring Security 的环境设置与基础配置(2024 最新版)(下)
13 0
|
4天前
|
安全 Java 数据库
第2章 Spring Security 的环境设置与基础配置(2024 最新版)(上)
第2章 Spring Security 的环境设置与基础配置(2024 最新版)
28 0
|
4天前
|
安全 Java API
第1章 Spring Security 概述(2024 最新版)(下)
第1章 Spring Security 概述(2024 最新版)
15 0

热门文章

最新文章