Spring-Spring Security


概述

Spring Security是一个安全管理框架,从Spring Boot开始对其提供了自动化配置方案,可以零配置使用Spring Security

引入Spring Security

1. Maven依赖

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-security</artifactId>
</dependency>

引入Maven依赖之后就会获得以下特性

  • 所有的HTTP请求都会进行身份验证
  • 使用HTTP基本的身份验证进行验证
  • 只有一个用户为: user,密码在启动时随机生成到控制台

配置Spring Security

Spring Security 提供了几种配置用户存储的选项

  • 基于内存的用户存储
  • 基于JDBC-based的用户存储
  • 基于LDAP-backed的用户存储
  • 自定义用户身份验证

只需自定义配置类继承WebSecurityConfigurerAdapter,重写configure方法进行设置,

同时需要注解@Configuration @EnableWebSecurity

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //配置用户存储选项
    }
}

1. 密码加密方式选择

  • BCryptPasswordEncoder—Applies bcrypt strong hashing encryption
  • NoOpPasswordEncoder—Applies no encoding
  • Pbkdf2PasswordEncoder—Applies PBKDF2 encryption
  • SCryptPasswordEncoder—Applies scrypt hashing encryption
  • StandardPasswordEncoder—Applies SHA-256 hashing encryption
  • 自定义加密实现PasswordEncoder即可

2. 基于内存的用户存储

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();//加密方式
    /** In-memory user store **/
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .passwordEncoder(passwordEncoder) //加密方式
                .withUser("Tom") //用户名
                .password(passwordEncoder.encode("123")) //密码
                .authorities("ROLE_USER") //权限
                .and()
                .withUser("Jerry")
                .password(passwordEncoder.encode("456"))
                .authorities("ROLE_USER");
    }
}

3. 基于JDBC-based的用户存储

  • 默认使用的用户查询SQL,不指定查询sql时spring会默认使用此sql查询

    public static final String DEF_USERS_BY_USERNAME_QUERY =
     "select username,password,enabled " +
     "from users " +
     "where username = ?";
    public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY =
     "select username,authority " +
     "from authorities " +
     "where username = ?";
    public static final String DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY =
     "select g.id, g.group_name, ga.authority " +
     "from groups g, group_members gm, group_authorities ga " +
     "where gm.username = ? " +
     "and g.id = ga.group_id " +
     "and g.id = gm.group_id";
  • 使用自定义的SQL查询

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Bean //使用@Bean注解则其他地方调用encoder()方法时会自动从SpringContext中查找对应的方法
        public PasswordEncoder encoder() {
             return new StandardPasswordEncoder("53cr3t");
        }
        /** JDBC-based user store **/
        @Autowired
        private DataSource dataSource;
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            //自定义查询(以下包含最少所需字段)
            String USER_QUERY = "SELECT USERNAME,PASSWORD,ENABLED FROM TEST_USERS WHERE USERNAME = ?";
            String AUTHORITY_QUERY = "SELECT USERNAME,AUTHORITY FROM TEST_AUTHORITIES WHERE USERNAME = ?";
            auth.jdbcAuthentication()
                    .dataSource(dataSource)
                    .usersByUsernameQuery(USER_QUERY) //自定义用户查询
                    .authoritiesByUsernameQuery(AUTHORITY_QUERY) //自定义用户权限查询
                    .passwordEncoder(encoder()); //设置加密方式,实际是从SpringContext中注入的bean
        }
    }

4. 基于LDAP-backed的用户存储

4.1 Maven依赖
  • 基本

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.ldap</groupId>
                <artifactId>spring-ldap-core</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-ldap</artifactId>
            </dependency>
    
            <!--嵌入式UnboundID服务器:LDAP数据库-->
            <dependency>
                <groupId>com.unboundid</groupId>
                <artifactId>unboundid-ldapsdk</artifactId>
            </dependency>
  • 也可采用嵌入式ApacheDS服务器: LDAP数据库

    <dependency>
        <groupId>org.apache.directory.server</groupId>
        <artifactId>apacheds-core</artifactId>
        <version>1.5.5</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.directory.server</groupId>
        <artifactId>apacheds-server-jndi</artifactId>
        <version>1.5.5</version>
        <scope>runtime</scope>
    </dependency>
4.2 LDAP方式一: 使用内嵌LDAP数据库+java方式
  • SecurityConfiguration.java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /** LDAP-backed user store **/
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.ldapAuthentication()
                //.userDnPatterns("uid={0},ou=people") //根据uid查找,{0}为占位符
                .userSearchBase("ou=people")
                .userSearchFilter("(uid={0})")
                .groupSearchBase("ou=groups")
                .contextSource()
                .root("dc=tacocloud,dc=com") //增加根节点(users.ldif文件中没有设置根节点)
                .ldif("classpath:users.ldif") //加载的ldap数据文件地址
                //.url("ldap://localhost:33389/dc=tacocloud,dc=com") //使用远程地址连接方式
                .and()
                .passwordCompare()
                .passwordEncoder(NoOpPasswordEncoder.getInstance())
                .passwordAttribute("userPassword");
    }
}
  • users.ldif文件

    #代码中使用.root("dc=tacocloud,dc=com")增加根节点,此处无需设置根节点
    
    #条目根节点: 组织
    dn: ou=groups,dc=tacocloud,dc=com
    objectclass: top
    objectclass: organizationalUnit
    ou: groups
    
    #条目根节点: 人员
    dn: ou=people,dc=tacocloud,dc=com
    objectclass: top
    objectclass: organizationalUnit
    ou: people
    
    #条目people下面的节点
    dn: uid=buzz,ou=people,dc=tacocloud,dc=com
    objectclass: top
    objectclass: person
    objectclass: organizationalPerson
    objectclass: inetOrgPerson
    cn: Buzz Lightyear
    sn: Lightyear
    uid: buzz
    userPassword: password
    
    #条目groups下面的节点
    dn: cn=tacocloud,ou=groups,dc=tacocloud,dc=com
    objectclass: top
    objectclass: groupOfNames
    cn: tacocloud
    member: uid=buzz,ou=people,dc=tacocloud,dc=com
4.3 LDAP方式二: <远程数据库连接类似>使用内嵌LDAP数据库+配置文件方式
  • SecurityConfiguration.java

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        /** LDAP-backed user store **/
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.ldapAuthentication()
                    .userDnPatterns("uid={0},ou=people") //根据uid查找,{0}为占位符
                    //.userSearchBase("ou=people")
                    //.userSearchFilter("(uid={0})")
                    .groupSearchBase("ou=groups")
                    .contextSource()
                    //.root("dc=tacocloud,dc=com")
                    //.ldif("classpath:users.ldif")
                    .url("ldap://localhost:33389/dc=tacocloud,dc=com") //远程连接方式连接
                    .and()
                    .passwordCompare()
                    .passwordEncoder(NoOpPasswordEncoder.getInstance())
                    .passwordAttribute("userPassword");
        }
    }
  • application.properties配置文件配置内嵌LDAP数据库

    # 数据库启动时加载的数据文件 # 根节点 # 端口
    spring.ldap.embedded.ldif=classpath:users.ldif
    spring.ldap.embedded.base-dn=dc=tacocloud,dc=com
    spring.ldap.embedded.port=33389
  • users.ldif文件

    # 需要在此额外加入根节点 
    dn: dc=tacocloud,dc=com
    objectclass: top
    objectclass: domain
    objectclass: extensibleObject
    dc: tacocloud
    
    #条目根节点: 组织
    dn: ou=groups,dc=tacocloud,dc=com
    objectclass: top
    objectclass: organizationalUnit
    ou: groups
    
    #条目根节点: 人员
    dn: ou=people,dc=tacocloud,dc=com
    objectclass: top
    objectclass: organizationalUnit
    ou: people
    
    #条目people下面的节点
    dn: uid=buzz,ou=people,dc=tacocloud,dc=com
    objectclass: top
    objectclass: person
    objectclass: organizationalPerson
    objectclass: inetOrgPerson
    cn: Buzz Lightyear
    sn: Lightyear
    uid: buzz
    userPassword: password
    
    #条目groups下面的节点
    dn: cn=tacocloud,ou=groups,dc=tacocloud,dc=com
    objectclass: top
    objectclass: groupOfNames
    cn: tacocloud
    member: uid=buzz,ou=people,dc=tacocloud,dc=com
4.4 LDAP基本语法简单介绍
  • LDAP是轻量级目录访问协议,LDAP数据库是以树结构存储的,查询效率高,但是修改慢

  • 使用LDAP是实现单点登录的一种方式,可以多个系统使用统一LDAP认证服务

# objectClass
# LDAP中,一个条目必须包含一个objectClass属性,且需要赋予至少一个值。每一个值将用作一条LDAP条目进行数据存储的模板;模板中包含了一个条目必须被赋值的属性和可选的属性。
# objectClass有着严格的等级之分,最顶层是top和alias。例如,organizationalPerson这个objectClass就隶属于person,而person又隶属于top。
# objectClass可分为以下3类:
# 结构型(Structural):如person和organizationUnit;
# 辅助型(Auxiliary):如extensibeObject;
# 抽象型(Abstract):如top,抽象型的objectClass不能直接使用。
# 对象类是属性的集合,LDAP预想了很多人员组织机构中常见的对象,并将其封装成对象类。比如人员(person)含有姓(sn)、名(cn)、电话(telephoneNumber)、密码(userPassword)等属性,单位职工(organizationalPerson)是人员(person)的继承类,除了上述属性之外还含有职务(title)、邮政编码(postalCode)、通信地址(postalAddress)等属性。
# 通过对象类可以方便的定义条目类型。每个条目可以直接继承多个对象类,这样就继承了各种属性。如果2个对象类中有相同的属性,则条目继承后只会保留1个属性。对象类同时也规定了哪些属性是基本信息,必须含有(Must 活Required,必要属性):哪些属性是扩展信息,可以含有(May或Optional,可选属性)。

# 在OpenLDAP的schema中定义了很多objectClass,下面列出部分常用的objectClass的名称。
# ● account
# ● alias
# ● dcobject
# ● domain
# ● ipHost
# ● organization
# ● organizationalRole
# ● organizationalUnit
# ● person
# ● organizationalPerson
# ● inetOrgPerson
# ● residentialPerson
# ● posixAccount
# ● posixGroup

# Attribute
# 属性(Attribute)类似于程序设计中的变量,可以被赋值。在OpenLDAP中声明了许多常用的Attribute(用户也可自己定义Attribute)。
# 每个条目都可以有很多属性(Attribute),比如常见的人都有姓名、地址、电话等属性。每个属性都有名称及对应的值,属性值可以有单个、多个,比如你有多个邮箱。
# 属性不是随便定义的,需要符合一定的规则,而这个规则可以通过schema制定。比如,如果一个entry没有包含在 inetorgperson 这个 schema 中的objectClass: inetOrgPerson,那么就不能为它指定employeeNumber属性,因为employeeNumber是在inetOrgPerson中定义的。
# 常见的Attribute含义如下:
# ● c:国家。
# ● cn:common name,指一个对象的名字。如果指人,需要使用其全名。
# ● dc:domain Component,常用来指一个域名的一部分。
# ● givenName:指一个人的名字,不能用来指姓。
# ● l:指一个地名,如一个城市或者其他地理区域的名字。
# ● mail:电子信箱地址。
# ● o:organizationName,指一个组织的名字。
# ● ou:organizationalUnitName,指一个组织单元的名字。
# ● sn:surname,指一个人的姓。
# ● telephoneNumber:电话号码,应该带有所在的国家的代码。
# ● uid:userid,通常指某个用户的登录名,与Linux系统中用户的uid不同。

# 下面列出部分常用objectClass要求必设的属性。
# ● account:userid。
# ● organization:o。
# ● person:cn和sn。
# ● organizationalPerson:与person相同。
# ● organizationalRole:cn。
# ● organizationUnit:ou。
# ● posixGroup:cn、gidNumber。
# ● posixAccount:cn、gidNumber、homeDirectory、uid、uidNumber。

5. 自定义用户身份验证

在此展示使用Spring JPA方式验证实例

5.1 domain: User.java
//域对象需实现UserDetails
@Data
@Entity
@Table(name = "TEST_USERS")
public class User implements UserDetails {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    private String username;
    private String password;
    private Boolean enabled;

    //返回授予用户权限集合,权限必须"ROLE_XXX"格式
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
    }
    //账号是否过期
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    //是否被锁
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    //凭证是否过期
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    //用户是否启用
    @Override
    public boolean isEnabled() {
        return enabled;
    }
}
5.2 Repository (DataSource配置和数据库驱动包引入参考Spring Jpa)
public interface UserRepository extends CrudRepository<User,Integer> {
    User findByUsername(String username);
}
5.3 UserDetailsService
/** 实现UserDetailsService接口 **/
@Service
public class UserRepositoryUserDetailsService implements UserDetailsService {
    private UserRepository userRepository;
    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //验证用户密码操作
        User user = userRepository.findByUsername(s);
        if(user == null) throw new UsernameNotFoundException("用户"+s+"不存在");
        return user;
    }
}
5.4 configuration中配置userDetailsService
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    public PasswordEncoder encoder(){
        return new BCryptPasswordEncoder();
    }
    @Qualifier("userRepositoryUserDetailsService") //指定具体哪一个对象@Service对应名称
    @Autowired
    private UserDetailsService userDetailsService;
    /** Customizing user authentication (使用自定义用户身份验证) **/ 
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
                .passwordEncoder(encoder());
    }
}

配置web请求

并不是所有的请求都需要进行身份验证,比如登录页面,注册页面等,因此可以通过配置实现以下功能

  • 满足安全条件的请求,服务器才能接收
  • 配置自定义登录页面
  • 可以使用户退出登录
  • 配置跨站点请求伪造保护
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
         //配置web请求
    }
}

1. 请求验证

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/home","/design")
                .hasRole("USER") //  /home 与 /design请求需验证"ROLE_USER"权限
                .antMatchers("/","/**").permitAll() //其他所有请求无需验证身份
                .and()
                .formLogin(); //使用表单校验
                //.loginPage("/login") 不指定或者不存在时使用Spring默认提供的/login页面
                //.and()
                //.httpBasic(); //验证方式httpBasic验证
    }
}    

2. antMatchers()返回对象API介绍(验证规则配置)

即ExpressionUrlAuthorizationConfigurer.AuthorizedUrl对象API

方法 描述
access(String) 使用SpEL表达式验证通过时才允许访问
anonymous() 允许匿名用户访问
authenticated() 允许通过身份验证的用户访问
denyAll() 无条件拒绝访问
fullyAuthenticated() 允许通过充分身份验证的用户访问(不含记住密码自动登录用户)
hasAnyAuthority(String…) 允许有指定权限种任何一种权限的用户访问
hasAnyRole(String…) 允许有指定角色中任何一个角色的用户访问
hasAuthority(String) 允许指定权限的用户访问
hasIpAddress(String) 允许指定IP地址的用户访问
hasRole(String) 允许指定角色的用户访问
http.authorizeRequests().antMatchers(“/home”).hasRole(“USER”)
not() 否则其他方法访问规则,not()后面指定
permitAll() 允许所有访问者
rememberMe() 允许通过remember-me认证的用户访问(即记住密码功能自动登录的用户)

3. SpEL表达式简介

  • 语法
Security expression 描述
authentication 用户身份验证对象
denyAll 永远返回false,
http.authorizeRequests().antMatchers(“/home”).access(“denyAll()”)
hasAnyRole(list of roles) 用户有任何给定角色则返回true
hasRole(role) 用户有给定角色则返回true
hasIpAddress(IP address) 请求来自给定的IP地址则返回true
isAnonymous() 是匿名用户则返回true
isAuthenticated() 用户通过身份验证则返回true
isFullyAuthenticated() 完全通过身份验证则返回true(不含remember-me认证)
isRememberMe() 使用remember-me认证通过则返回true
permitAll 永远为true
principal 包含用户主要属性的对象
  • 实例
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // "/home"请求验证: 拥有ROLE_USER权限并且日期是周二才能通过
        http.authorizeRequests()
                .antMatchers("/home")
                .access("hasRole('USER') && "+
                        "T(java.time.LocalDate).now().getDayOfWeek().getValue() == "+
                        "T(java.time.DayOfWeek).TUESDAY"
                )
                .and()
                .formLogin();
    }

4. 自定义登录页面

  • Controller: LoginController.java

    //因为都是最简单的Controller,此种方式更简便
    @Configuration
    public class LoginController implements WebMvcConfigurer {
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/login").setViewName("security/login");
            registry.addViewController("/success").setViewName("security/success");
        }
    }
  • page

    • login.html

      <!DOCTYPE html>
      <html lang="en" xmlns:th="http://www.thymeleaf.org">
      <head>
          <meta charset="UTF-8">
          <title>Login</title>
      </head>
      <body>
      <form th:action="@{/loginUrl}" method="post">
          <input type="text" name="name" placeholder="Username"/><br/>
          <input type="password" name="pwd" placeholder="Password"/><br/>
          <input type="submit" value="登录" />
      </form>
      </body>
      </html>
    • success.html

      <!DOCTYPE html>
      <html lang="en" xmlns:th="http://www.thymeleaf.org">
      <head>
          <meta charset="UTF-8">
          <title>success</title>
      </head>
      <body>
      <h1>Successful!</h1>
      </body>
      </html>
  • security配置: SecurityConfiguration.java

    @Configuration
    @EnableWebSecurity
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .antMatchers("/home") //   /home请求需要进行验证
                    .hasRole("USER")
                    .and()
                    .formLogin() //使用表单校验
                    .loginPage("/login") //自定义登录页面
                    .loginProcessingUrl("/loginUrl") //设置登录页面请求的url路径(默认为/login)
                    .usernameParameter("name")//自定义登录页面用户名参数名:默认为username
                    .passwordParameter("pwd") //自定义登录页面密码参数名: 默认为password
                    .defaultSuccessUrl("/success",true);//定义验证成功时跳转url地址,true表示强制跳转
                    //.and()
                    //.logout() //登出配置: 同上(默认使用 /logout请求)
                    //.logoutSuccessUrl("/"); //登出成功跳转页面
        }
    }    

5. 防止跨站点请求伪造(CSRF)

Spring Security默认开启了CSRF保护,通过CSRF token实现的(默认保存在session中),只需要在表单中增加字段_csrf即可

  • 如果页面使用Spring MVC 的jsp标签库或者带有Spring Security方言的Thymeleaf,

    <form th:action="@{/loginUrl}">,form标签中使用了Thymeleaf属性,此时页面无需添加任何操作,提交时Spring 会自动添加_csrf字段

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Login</title>
    </head>
    <body>
    <form th:action="@{/loginUrl}" method="post">
        <!-- 无需额外添加_csrf字段 -->
        <input type="text" name="name" placeholder="Username"/><br/>
        <input type="password" name="pwd" placeholder="Password"/><br/>
        <input type="submit" value="登录" />
    </form>
    </body>
    </html>
  • 手动添加_csrf字段

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Login</title>
    </head>
    <body>
    <form action="/loginUrl" method="post"> 
        <!-- form标签没有使用th:action此种Thymeleaf属性时需手动引入 -->
        <input type="hidden" name="_csrf" th:value="${_csrf.token}"/>
        <input type="text" name="name" placeholder="Username"/><br/>
        <input type="password" name="pwd" placeholder="Password"/><br/>
        <input type="submit" value="登录" />
    </form>
    </body>
    </html>
  • 前后端分离时,以纯html为例

    因为CSRF token默认保存在session中,此时前端无法获取,需要更改Spring Security配置保存到cookie中并设置js可读

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Login</title>
        <script>
            //读取cookie信息
            function getCookie(name) {
                var arr,reg=new RegExp("(^| )"+name+"=([^;]*)(;|$)");
                if(arr=document.cookie.match(reg))
                    return unescape(arr[2]);
                else
                    return null;
            }
            window.onload = function(){
                //_csrf字段值设置为cookie中保存的token值
                document.querySelector("#csrf").value = getCookie("XSRF-TOKEN")
            }
        </script>
    </head>
    <body>
    <form action="/loginUrl" method="post">
        <!-- 增加_csrf字段 -->
        <input type="hidden" name="_csrf" id = "csrf"/><br/>
        <input type="text" name="name" placeholder="Username"/><br/>
        <input type="password" name="pwd" placeholder="Password"/><br/>
        <input type="submit" value="登录" />
    </form>
    </body>
    </html>
    //Spring Security配置
    @Configuration
    @EnableWebSecurity
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf()//CSRF配置
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); //token保存在cookie中,并且设置js可读
        }
    } 
  • 禁用csrf

    //Spring Security配置
    @Configuration
    @EnableWebSecurity
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf()//CSRF配置
                .disable(); //禁用
        }
    } 

6. 获取验证后的用户

6.1. 通过Principal对象

Principal对象封装了验证用户主要属性

    //Controller中
    @RequestMapping("/success")
    public String success(Principal principal){
        //通过Principal对象获取到验证用户名,然后再根据用户名从数据库中查找该用户
        User user = userRepository.findByUsername(principal.getName());
        System.out.println(user);
        return "security/success";
    }
6.2. 通过Authentication对象

Authentication对象封装了用户身份验证信息

    @RequestMapping("/success")
    public String success(Authentication authentication){
        //通过 authentication 对象获取到验证用户
        User user = (User) authentication.getPrincipal();
        System.out.println(user);
        return "security/success";
    }
6.3. 通过@AuthenticationPrincipal注解(推荐)
    @RequestMapping("/success")
    public String success(@AuthenticationPrincipal User user){
        //通过 @AuthenticationPrincipal 注解获取到验证用户
        System.out.println(user);
        return "security/success";
    }
6.4. SecurityContext对象

优点: 不仅可以在Controller中使用, 还可以在Spring application中任何地方使用

    @RequestMapping("/success")
    public String success(){
        //通过SecurityContextHolder获取到SecurityContext对象
        //再获取Authentication对象,然后再获取到验证用户
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        User user = (User)authentication.getPrincipal();
        System.out.println(user);
        return "security/success";
    }

文章作者: Bryson
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Bryson !
评论
 上一篇
Spring-使用配置属性 Spring-使用配置属性
概述 微调自动配置的beans 应用程序组件配置 使用Spring profiles 对自动配置进行微调1. Spring环境 Spring环境是所有配置属性的一站式商店,它抽象了配置属性的起源,以便需要这些属性的bean可以直接从S
2020-05-21
下一篇 
设计模式-解释器模式 设计模式-解释器模式
解释器模式的定义 *解释器(Interpreter)模式: * 提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等 是一种不常用的设
2020-05-14
  目录