Skip to content

SpringSecurity框架基本使用

入门案例

第一步 创建Springboot工程

第二部 引入相关依赖

xml
<!--SpringSecurity 依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

第三步 编写controller进行测试

java
@RestController
@RequestMapping("/test")
public class TestController {
    @GetMapping("hello")
    String add(){
        return "Hello securty";
    }
}

默认用户名:admin

默认密码:

image-20220821143644665

SpringSecurity 本质是一个过滤器链 他有很多过滤器

先重点看三个过滤器:

FilterSecurityInterceptor方法级的权限过滤器,基本位于过滤器链的最底部。

ExceptionTranslationFilter异常过滤器,用来处理在认证授权过程中抛出的异常

UsernamePasswordAuthenticationFilter对 /login 的POST请求做拦截,校验表单中的用户名,密码。

过滤器如何进行加载

SpringBoot对于SpringSecurity提供了自动化配置方案

  1. 使用SpringSecurity配置过滤器
    • DelegatingFilterProxy
UserDetailsService接口

当你什么也没有配置的时候,账号密码是由SpringSecurity定义生成的。而在实际项目中账号和密码都是从数据库中查询出来的。所以我们要通过自定义逻辑控制认证逻辑。

如果需要自定义逻辑时,只需要实现UserDetailsService接口即可。定义接口如下:

过程:

  1. 创建一个类并继承UsernamePasswordAuthenticationFilter,重写三个方法
  2. 创建类实现UserDetailsService,编写查询数据过程,返回User对象,这个User对象是安全框架提供对象
PasswordEncoder接口

数据加密接口,用于返回User对象里面密码加密。

Web权限方案

1.认证

2.授权

  1. 设置登陆的用户名和密码

通过配置文件

properties
spring.security.user.name=admin
spring.security.user.password=123456

通过配置类

java
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String password = passwordEncoder.encode("123123");
        auth.inMemoryAuthentication().withUser("admin").password(password).roles("admin");
    }

    @Bean
    PasswordEncoder password(){
        return new BCryptPasswordEncoder();
    }
}

自定义编写实现类(重要)

代码实现(版本过时)
第一步 创建配置类

设置使用哪个userDetailsService实现类

java
@Configuration
public class SecurityConfigWeb extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        auth.userDetailsService(userDetailsService).passwordEncoder(password());
    }
    @Bean
    PasswordEncoder password(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception{
        /**
         * 自定义登录页
         */
        http.formLogin()
                .loginPage("/login.html")//登录页设置
                .loginProcessingUrl("/user/login") //登录访问路径
                .defaultSuccessUrl("/test/index").permitAll()//登录成功后跳转路径
                .and().authorizeRequests()
                .antMatchers("/","/test/hello").permitAll()//设置不需要认证的路径
                .anyRequest().authenticated()//所有请求都能访问
                .and().csrf().disable(); //关闭csrf防护
    }
}
第二步 编写实现类

返回User对象,User对象有用户名密码和操作权限

java
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
    @Autowired
    UsersMapper usersMapper;//注入实体类

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据username查询数据
        Users users = new  LambdaQueryChainWrapper<>(usersMapper).eq(Users::getUsername,username).one();
        //找不到用户则抛出异常
        if (users == null)
            throw new UsernameNotFoundException("用户不存在");
        //创建一个权限给用户  这里的 “role”表示用户的角色权限
        List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
        //返回security的User
        return new User("tt",new BCryptPasswordEncoder().encode(users.getPassword()),authorities);
    }
}

基于角色或权限进行访问控制

方法一:hasAuthority()

针对某一个权限

.antMatchers("/test/index","/admin/index").hasAuthority("admins")

↑ 需要admins权限才能访问路径

修改配置类

.hasAuthority("admins")

java
@Configuration
public class SecurityConfigWeb extends WebSecurityConfigurerAdapter {
    (其他代码...)

    @Override
    protected void configure(HttpSecurity http) throws Exception{
        /**
         * 自定义登录页
         */
        http.formLogin()
                .loginPage("/login.html")//登录页设置
                .loginProcessingUrl("/user/login") //登录url
            	//.permitAll()表示所有权限都可访问
                .defaultSuccessUrl("/test/index").permitAll()//登录成功后跳转url 
                .and().authorizeRequests()
                .antMatchers("/","/test/hello").permitAll()//设置路径所有人访问
            
                //当前登陆的用户,有下列权限的才可以访问
                .antMatchers("/test/index").hasAuthority("admins") //设置路径需要的权限
                .anyRequest().authenticated()//所有请求都能访问
                .and().csrf().disable(); //关闭csrf防护
    }
}

image-20220821174521176

方法二:hasAnyAuthority()

针对多个权限

.antMatchers("/test/index","/admin/index").hasAuthority("admins,role")

↑ 需要admins或者role权限才能访问路径

修改配置类

.hasAnyAuthority("admins,role")

java
@Configuration
public class SecurityConfigWeb extends WebSecurityConfigurerAdapter {
    (其他代码...)

    @Override
    protected void configure(HttpSecurity http) throws Exception{
        /**
         * 自定义登录页
         */
        http.formLogin()
                .loginPage("/login.html")//登录页设置
                .loginProcessingUrl("/user/login") //登录url
            	//.permitAll()表示所有权限都可访问
                .defaultSuccessUrl("/test/index").permitAll()//登录成功后跳转url 
                .and().authorizeRequests()
                .antMatchers("/","/test/hello").permitAll()//设置路径所有人访问
            
                //当前登陆的用户,有下列权限的才可以访问
                .antMatchers("/test/index").hasAnyAuthority("admins,role") //设置路径需要的权限
                .anyRequest().authenticated()//所有请求都能访问
                .and().csrf().disable(); //关闭csrf防护
    }
}

自定义403页面

修改添加访问配置类

java
//        配置自定义403页面
        http.exceptionHandling().accessDeniedPage("/unauth.html");

添加对应控制器

常用认证授权注解使用

@EnableGlobalMethodSecurity(securedEnabled = true)

@Secured

@Secured({"ROLE_normal","ROLE_管理员"})

@PreAuthorize

@PostFilter

@PreFilter

用户注销操作

在配置类中修改
java
        /**
         * 用户登出
         */
        http.logout().logoutUrl("/logout").logoutSuccessUrl("/test/hello").permitAll();

html

html
<a href="/logout">退出登录</a>

自动登录(记住我)

一:创建数据库

image-20220828104445369

二:配置类

java
    @Autowired
    private DataSource dataSource;//注入数据源
    //配置对象
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
//        jdbcTokenRepository.setCreateTableOnStartup(true);//启动时自动建表,没有表的情况下
        return jdbcTokenRepository;
    }

三:配置类配置

java
                .and().rememberMe().tokenRepository(persistentTokenRepository())
                .tokenValiditySeconds(60)//设置有效时间 秒
                .userDetailsService(userDetailsService)

四:登陆页面添加复选框(remember-me)

html
记住我:<input type="checkbox" name="remember-me">

csrf跨站请求伪造

SpringSecurity4+ 默认开启CSRF

关闭CSRF : http.csrf().disable(); //关闭csrf防护

html

html
<form action="/user/login" method="post">
    <input type="hidden" th:name="${_csrf:parameterName}" th:value="${_csrf:token}">
    用户名:<input type="text" name="username" id=""><br>
    密码:<input type="password" name="password" id=""><br>
    记住我:<input type="checkbox" name="remember-me">
    <button type="submit">登录</button>
</form>

完整代码示例

用户实体类

java
@Data
@TableName("user")
public class Users {
    Long id;
    String username;
    String password;
}

Mapper接口

java
@Mapper
public interface UsersMapper extends BaseMapper<Users> {
}

config配置

java
@Configuration
public class SecurityConfigWeb extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;

    //注入数据源
    @Autowired
    private DataSource dataSource;
    //配置对象 ,记住登录
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
//        jdbcTokenRepository.setCreateTableOnStartup(true);//启动时自动建表 仅第一次开启
        return jdbcTokenRepository;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        auth.userDetailsService(userDetailsService).passwordEncoder(password());
    }
    @Bean
    PasswordEncoder password(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception{

        //配置url访问权限
        http.authorizeRequests()
                .antMatchers("/test/hello").permitAll()//设置路径所有人访问
                //1 有下列权限的才可以访问 hasAuthority方法
//                .antMatchers("/test/index").hasAuthority("admins")
                //2 有下列权限的才可以访问 hasAnyAuthority方法
//                .antMatchers("/test/index").hasAnyAuthority("admins,sale,staff")
                //3 有下列角色的才可以访问 hasRole方法   ROLE_sales
                .antMatchers("/test/index").hasRole("sale")
                //4 有下列角色的才可以访问 hasRole方法   ROLE_sales
                .antMatchers("/test/index","/test/test").hasAnyRole("ROLE_sale,ROLE_staff")
                .anyRequest().authenticated();//所有请求 都 需要身份验证

        //配置url的访问权限
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/**update**").permitAll()
                .antMatchers("/login/**").permitAll()
                .anyRequest().authenticated();

        
        
        //关闭csrf防护
        http.csrf().disable();

        //自定义登录页
        http.formLogin()
                .loginPage("/login.html")//登录页面设置
                .loginProcessingUrl("/user/login") //登录url(post表单提交url)
            	//登录成功后跳转url或页面 .permitAll()表示所有权限都可访问
                .defaultSuccessUrl("/logout.html").permitAll()
            //记住登录设置
                .and().rememberMe().tokenRepository(persistentTokenRepository())
                .tokenValiditySeconds(60)//设置有效时间 秒
                .userDetailsService(userDetailsService);

        //用户登出
        http.logout().logoutUrl("/logout").logoutSuccessUrl("/test/hello").permitAll();

        // 配置自定义403页面
        http.exceptionHandling().accessDeniedPage("/unauth.html");
    }
}

service

java
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
    @Autowired
    UsersMapper usersMapper;//注入实体类

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据username查询数据
        Users users = new  LambdaQueryChainWrapper<>(usersMapper).eq(Users::getUsername,username).one();
        //找不到用户则抛出异常
        if (users == null)
            throw new UsernameNotFoundException("用户不存在");
        //创建一个权限
        List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_admins");
        //返回security的User
        return new User(users.getUsername(),new BCryptPasswordEncoder().encode(users.getPassword()),authorities);
    }
}

HTML登录页

路径:resources/static/login.html

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/user/login" method="post">
    <input type="hidden" th:name="${_csrf:parameterName}" th:value="${_csrf:token}">
    用户名:<input type="text" name="username" id=""><br>
    密码:<input type="password" name="password" id=""><br>
    记住我:<input type="checkbox" name="remember-me">
    <button type="submit">登录</button>
</form>
</body>
</html>

HTML登出

<a href="/logout">登出</a>

HTML自定义无权限页

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>无权限!</h1>
</body>
</html>

SpringSecurity+Jwt前后端分离

引入SpringSecurity

xml
<!--SpringSecurity 依赖-->
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-security</artifactId>
     </dependency>

大概逻辑流程

微服务权限方案

1、什么是微服务

HTTP API

微服务架构风格是一种使用一套小服务来开发单个应用的方式途径,每个服务运行在自己的进程中,并使用轻量级机制通信,通常是HTTP API,这些服务基于业务能力构建,并能够通过自动化部署机制来独立部署,这些服务使用不同的编程语言实现,以及不同数据存储技术,并保持最低限度的集中式管理。

微服务优势:

(1)微服务每个模块就相当于-一个单独的项目,代码量明显减少,遇到问题也相对来说比较好解决. (2)微服务每个模块都可以使用不同的存储方式(比如有的用redis,有的用mysal等数据库也是单个模块对应自己的数据库。 (3)微服务每个模块都可以使用不同的开发技术,开发模式更灵活。

微服务本质:

(1)微服务,关键其实不仅仅是微服务本身,而是系统要提供一 套基础的架构,这种架构使得微服务可以独立的部署、运行、升级,不仅如此,这个系統架构还让微服务与微服务之间在结构上“松耦合”,而在功能上则表现为一个统一的整体。这种所谓的“统一的整体”表现出来的是统一风格的界面, 统一的权限管理, 统一的安全策略,统一的上线过程,统一的日志和审计方法,统一的调度方式,统一的访问入口等等。 (2)微服务的目的是有效的拆分应用,实现敏捷开发和部署。

2、微服务认证和授权实现过程

(1)如果是基于Session, 那么Spring-security会对cookie里的sessionid进行解析,找到服务器存储的session信息,然后判断当前用户是否符合请求的要求。 (2)如果是token,则是解析出token,然后将当前请求加入到Spring-security管理的权限信息中去。

image-20220830154926522

3、基于SpringSecurity认证授权方案

搭建项目工程

1.创建父工程

创建父工程 acl_parent : 管理依赖版本

2.在父工程创建子模块

  1. common
    • service_base : 工具类
    • spring_security : 权限配置
  2. infrastructure
    • api_gateway : 网关
  3. service
    • service_acl : 权限管理微服务模块
(1)编写common所需工具类