SpringSecurity安全框架

这篇文章资源来自于尚硅谷,你可以从本文中学习到如下知识

  • 首先这篇文章适合有一定的SpringSecurity的项目经验的人学习
  • 第二这篇文章重点在让你理解SpringSecurity的原理,不是以项目为驱动
  • 第三你可以本文中学习到一定的知识
    • SpringSecurity的原理,重点注解
    • 理解SpringSecurity能干嘛,底层原理。
    • 等等~~~~~~~~

第一部分

一、了解部分

1、SpringSecurity简介

这么说吧,它是一款非常牛皮的安全框架,主要是干两个事情,认证(Authentication)授权(Authorization)

2、历史

它的历史,自己可以去搜索看一下,不讲了

3、优点

SpringSecurity 特点:

  • 和 Spring 无缝整合。
  • 全面的权限控制。
  • 专门为 Web 开发而设计。
    • 旧版本不能脱离 Web 环境使用。
    • 新版本对整个框架进行了分层抽取,分成了核心模块和 Web 模块。单独
      引入核心模块就可以脱离 Web 环境。
  • 重量级。

4、同级别的框架

shiro,这个也是我后续会出的文章

5、搭配

shiro + ssm更好
SpringSecurity + SpringBoot/Spring Cloud

6、核心模块

在这里插入图片描述

二、案例

1、编写类

一定要记住下面这几类,大概就会伴随我们这一篇文章

  • WebSecurityConfigurerAdapter:自定义Security策略
  • AuthenticationManagerBuilder:自定义认证策略
  • @EnableWebSecurity:开启WebSecurity模式
@Configuration
public class SecurityConfigextends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
 http.formLogin() // 表单登录
.and()
 .authorizeRequests() // 认证配置
.anyRequest() // 任何请求
.authenticated(); // 都需要身份验证
}
}

2、重要的单词

1、主体:principal
使用系统的用户或设备或其他系统远程系统登录的用户等。大致就是谁使用系统,谁就时主体。
2、认证:authentication
权限管理系统确认一个主体的身份,允许主体进入系统。简单来说就是认证了能登录。
3、授权:authorization
就是你有什么样子的角色,然后你会有什么权利

3、SpringSecurity的原理

SpringSecurity本质是一个过滤链:
从启动是可以获取到过滤器链:

org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFil
ter
org.springframework.security.web.context.SecurityContextPersistenceFilter 
org.springframework.security.web.header.HeaderWriterFilter
org.springframework.security.web.csrf.CsrfFilter
org.springframework.security.web.authentication.logout.LogoutFilter 
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter 
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter 
org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter
org.springframework.security.web.savedrequest.RequestCacheAwareFilter
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
org.springframework.security.web.authentication.AnonymousAuthenticationFilter 
org.springframework.security.web.session.SessionManagementFilter 
org.springframework.security.web.access.ExceptionTranslationFilter 
org.springframework.security.web.access.intercept.FilterSecurityInterceptor

重点有三个:

(1)FilterSecurityInterceptor

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

  • 康康这个
    在这里插入图片描述

    • super.beforeInvocation(fi) 表示查看之前的 filter 是否通过。
    • fi.getChain().doFilter(fi.getRequest(), fi.getResponse());表示真正的调用后台的服务。

(2)ExceptionTranslationFilter

  • 是一个异常过滤器,用来处理在认证授权过程中抛出的异常
  • 看这个
    在这里插入图片描述

(3)UsernamePasswordAuthenticationFilter

  • :对/login 的 POST 请求做拦截,校验表单中用户名,密码
  • 来看这个
    在这里插入图片描述
  • 里面有一个方法:返回是UserDetails,是系统默认的用户(即:pincipal),当然我们也会编写继承自这个类的类

(4)单独来讲一下 UserDetailsService

这个类提供有一个方法,它会和我们的
principal和UserDetails有关系的,这里只要知道这个就好了,我们一般会编写一个User来实现UserDetails,配置SpringSecurity实现安全管理

public class User implements UserDetails {
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public String getPassword() {
        return null;
    }

    @Override
    public String getUsername() {
        return null;
    }

    @Override
    public boolean isAccountNonExpired() {
        return false;
    }

    @Override
    public boolean isAccountNonLocked() {
        return false;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return false;
    }

    @Override
    public boolean isEnabled() {
        return false;
    }
}

(5)PasswordEncoder接口讲解

1、接口大概情况

public interface PasswordEncoder {
	// 表示把参数按照特定的解析规则进行解析
    String encode(CharSequence var1);

	// 表示验证从存储中获取的编码密码与编码后提交的原始密码是否匹配。如果密码匹
配,则返回 true;如果不匹配,则返回 false。第一个参数表示需要被解析的密码。第二个
参数表示存储的密码。
    boolean matches(CharSequence var1, String var2);

	// 表示如果解析的密码能够再次进行解析且达到更安全的结果则返回 true,否则返回
false。默认返回 false。
    default boolean upgradeEncoding(String encodedPassword) {
        return false;
    }
}

2、实现类
实现上面接口的有几个类,但是最出色的是下面这个类

  • BCryptPasswordEncoder 是 Spring Security 官方推荐的密码解析器,平时多使用这个解析
    器。
  • BCryptPasswordEncoder 是对 bcrypt 强散列方法的具体实现。是基于 Hash 算法实现的单
    向加密。可以通过 strength 控制加密强度,默认 10.

3、测试

@Test
public void test01(){
// 创建密码解析器
BCryptPasswordEncoder bCryptPasswordEncoder = new 
BCryptPasswordEncoder();
// 对密码进行加密
String atguigu = bCryptPasswordEncoder.encode("atguigu");
// 打印加密之后的数据
System.out.println("加密之后数据:\t"+atguigu);
//判断原字符加密后和加密之前是否匹配
boolean result = bCryptPasswordEncoder.matches("atguigu", atguigu);
// 打印比较结果
System.out.println("比较结果:\t"+result);
}

三、接下来就开始编写代码理解了

(一)实现认证

1、第一种是根据内存的认证

这种的话比较简单,而且后续也基本不会使用,我这就不写了,如果想要了解的,可以看我主页的另外一个篇文章的,里面是跟着狂神说的课程来写的,所有请移步到我的主页(手动狗头)

2、基于数据库的登录认证流程

(1)首先准备核心类(User类)

public class User implements UserDetails {
    private Integer id;
    private String username;
    private String password;
    private Boolean enabled;
    private Boolean locked;
    private List<Role1> roles;

    //这个方法就是辅助查找角色的
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        for (Role1 role: roles) {
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }
        return authorities;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return !locked;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return enabled;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }

    public void setLocked(Boolean locked) {
        this.locked = locked;
    }

    public void setRoles(List<Role1> roles) {
        this.roles = roles;
    }
}

(2)第二就是准备User类Service层

@Service
public class UserService implements UserDetailsService {
    @Autowired
    UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userMapper.loadUsernameByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("账号不存在");
        }
        user.setRoles(userMapper.getUserRolesByUid(user.getId()));
        return user;
    }
}

这里小总结一波

上面这个类要注意下面几点

1、第一点

  • User user = userMapper.loadUsernameByUsername(username);这句话是返回的是我们上面编写的User,这个类必须继承自UserDetails
  • 这个类可以是SpringSecurity提供的import org.springframework.security.core.userdetails.User;
  • 这个loadUserByUsername是给我们编写的LoginController调用进行判断的

2、第二就是大致的认证流程

  • 第一步是前端发请求过来
  • 然后就是在LoginController里面进行判断
  • 判断后会去调用我们的登录方法
  • 然后就是放行不放行的要认证的结果判断

(3)然后就是WebSecurityConfigurerAdapter配置

既然继承了WebSecurityConfigurerAdapter了,继承来的方法比较常用的方法有

  • configure(HttpSecurity http)是进行放行呀,还有其他的配置,后续细讲
  • configure(WebSecurity web) ,这个在我的印象中常用的是进行静态资源放行,后续细讲
  • configure(AuthenticationManagerBuilder auth) 这个就是围绕我们的角色呀,什么的配置
  • 要注意@EnableWebSecurity,这是开启使用WebSecurity配置,假设我们要开启什么的时候就是Enable + zzz的形式,这个是设计SpringBoot全面接管MVC的事情。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
    }
    
}

__(1、configure(HttpSecurity http) 到底能配置啥

@Override

protected void  configure(HttpSecurity http) throws Exception {

http

.authorizeUrls()

.antMatchers("/signup","/about").permitAll() // #4

.antMatchers("/admin/**").hasRole("ADMIN") // #6

.anyRequest().authenticated() // #7

.and()

.formLogin() // #8

.loginUrl("/login") // #9

.permitAll(); // #5

}

}
  • #1 可以在内存中的验证(memory authentication)叫作”user”的用户
  • #2 可以在内存中的验证(memory authentication)叫作”admin”的管理员用户
  • #3 忽略任何以”/resources/”开头的请求,这和在XML配置http@security=none的效果一样
  • #4 任何人(包括没有经过验证的)都可以访问”/signup”和”/about”
  • #5 任何人(包括没有经过验证的)都可以访问”/login”和”/login?error”。permitAll()是指用户可以访问formLogin()相关的任何URL。
  • #6 “/admin/”开头的URL必须要是管理员用户,譬如”admin”用户
  • #7 所有其他的URL都需要用户进行验证
  • #8 使用Java配置默认值设置了基于表单的验证。使用POST提交到”/login”时,需要用”username”和”password”进行验证。
  • #9 注明了登陆页面,意味着用GET访问”/login”时,显示登陆页面

上面部分参考文章

上面是比较常用的:下面是我们自己来学习一下:

@Override
    protected void configure(HttpSecurity http) throws Exception {
        //认证策略配置

        //这个是关闭跨域的请求,通俗点就是假设你自己要开发登录页面就要关闭
        http.csrf().disable();
        //下面是开启表单的配置
        http.formLogin()
                .loginPage("/index")//设置那个是登录的页面
                .loginProcessingUrl("/login")//设置哪个是登录的url
                .successForwardUrl("/success")//登录成功之后跳转到哪个url
                .failureForwardUrl("/fail")//登录失败后要跳转的url,感觉比较常用的配置就是这些了
                .usernameParameter("abc")//获取登录用户名,
                .passwordParameter("pwd");//获取登录密码

        //下面就是认证策略的配置了
        http.authorizeRequests()
                    .antMatchers("/xxx/**","/yyy/**")
                    //.hasRole("xx")//如果是点的这个就是要有xx的角色才能访问"/xxx/**","/yyy/**"路径下的资源
                    .hasRole("")//如果这样配置就是可以所有的人都可以访问
                    .antMatchers("/yyyyyy")
                    .permitAll()//表示yyy下面的所有都可以访问
                    .anyRequest()
                    .authenticated();//anyRequest()authenticated()这两句话表示其他的哟啊认证后访问
        http.exceptionHandling()
                .accessDeniedHandler(new AccessDeniedHandler() {
                    @Override
                    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {

                    }
                })
                .authenticationEntryPoint(new AuthenticationEntryPoint() {
                    @Override
                    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {

                    }
                });
                //accessDeniedHandler这个简单点来说就是自定义的如果没有权限就会跳到这里来,可以编写为一个类
                //authenticationEntryPoint这个就是我们未登录呀,都会跳到这个页面中来

        http.addFilterBefore(new xxx());//这里是配置自己编写的过滤器呀就是在这里配置
    }

__(2、configure(AuthenticationManagerBuilder auth) 到底能配置啥

@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //这个里面就是对我们的角色的配置,一般的数据登录就在这里
        auth
                .inMemoryAuthentication()
                .withUser("admin").password("admin").roles("USER");//这种是内存认证的情况
    }

  

__(3、configure(WebSecurity web)到底能配置啥

  @Override
    public void configure(WebSecurity web) throws Exception {
        //下面就是配置什么静态资源呀,就是要放行的,不用登录可以访问的
        web.ignoring().antMatchers("/xx","/xx");
    }

(4)认证方式

如果想要具体的可以去看这篇文章:文章链接

3、下面就是权限认证(角色认证)

以下认证必须基于数据库的认证

(1)角色认证方法

在这里插入图片描述
1、hasAuthority方法,.antMatchers("/find").hasAuthority("")
如果当前的主题具有指定的权限,则返回true
在这里插入图片描述
2、hasAnyAuthority 方法,.antMatchers("/find").hasAnyAuthority("")

  • 如果当前的主体有任何提供的角色(给定的作为一个逗号分隔的字符串列表)的话,返回
    true.

3、 hasRole方法,.antMatchers("/find").hasRole("");

  • 如果用户具备给定角色就允许访问,否则出现 403。
  • 如果当前主体具有指定的角色,则返回 true。

4、hasAnyRole方法,.antMatchers("").hasAnyRole()

  • 表示用户具备任何一个条件都可以访问。
    给用户添加角色:

(2)基于数据库的角色认证方式

1、第一步准备自己的实体类

@Data
public class Menu {
private Long id;
private String name;
private String url;
private Long parentId;
private String permission;
}
@Data
public class Role {
private Long id;
private String name;

2、第二步就是编写Mapper层

/**
* 根据用户 Id 查询用户角色
* @param userId
* @return
*/
List<Role> selectRoleByUserId(Long userId);
/**
* 根据用户 Id 查询菜单
* @param userId
* @return
*/
List<Menu> selectMenuByUserId(Long userId)
需要在 resource/mapper 目录下自定义 UserInfoMapper.xml 
<?xml version="1.0"encoding="utf-8"?>
<!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 
3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.mapper.UserInfoMapper">
<!--根据用户 Id 查询角色信息-->
<select id="selectRoleByUserId"resultType="com.atguigu.bean.Role">
 SELECT r.id,r.name FROM role r INNER JOIN role_user ru ON 
ru.rid=r.id where ru.uid=#{0}
</select>
<!--根据用户 Id 查询权限信息-->
<select id="selectMenuByUserId"resultType="com.atguigu.bean.Menu">
 SELECT m.id,m.name,m.url,m.parentid,m.permission FROM menu m
 INNER JOIN role_menu rm ON m.id=rm.mid
INNER JOIN role r ON r.id=rm.rid
INNER JOIN role_user ru ON r.id=ru.rid
WHERE ru.uid=#{0}
</select>
</mapper>

3、编写实现类
在这里插入图片描述

4、配置文件中放行:application.properteis添加
mybatis.mapper-locations=classpath:mapper/*.xml
也可以像下面这样在application.yaml 里面配置
在这里插入图片描述
5、最后修改配置
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值