• 项目1-springsecutiry


    SpringSecurity

    简介

    使用方式(这里用第三种方式)

    • 一种是全部利用配置文件,将用户、权限、资源(url)硬编码在xml文件中

    • 二种是用户和权限用数据库存储,而资源(url)和权限的对应采用硬编码配置

    • 三种是细分角色和权限,并将用户、角色、权限和资源均采用数据库存储,并且自定义过滤器,代替原有的FilterSecurityInterceptor过滤器, 并分别实现AccessDecisionManager、InvocationSecurityMetadataSourceService和UserDetailsService,并在配置文件中进行相应配置。

    SpringSecurity-HelloWorld

    测试环境搭建

    pom配置

           <dependency>
    
                         <groupId>org.springframework</groupId>
    
                         <artifactId>spring-webmvc</artifactId>
    
                         <version>4.3.20.RELEASE</version>
    
                  </dependency>
    
                  <dependency>
    
                         <groupId>javax.servlet.jsp</groupId>
    
                         <artifactId>jsp-api</artifactId>
    
                         <version>2.2</version>
    
                         <scope>provided</scope>
    
                  </dependency>
    
                  <dependency>
    
                         <groupId>javax.servlet</groupId>
    
                         <artifactId>servlet-api</artifactId>
    
                         <version>2.5</version>
    
                         <scope>provided</scope>
    
                  </dependency>
    
                  <dependency>
    
                         <groupId>javax.servlet</groupId>
    
                         <artifactId>jstl</artifactId>
    
                         <version>1.2</version>
    
                  </dependency>
    
    
    

    XML配置

    <servlet>
    
    <servlet-name>springDispatcherServlet</servlet-name>
    
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    
    <init-param>
    
    <param-name>contextConfigLocation</param-name>
    
    <param-value>classpath:spring.xml</param-value>
    
    </init-param>
    
    <load-on-startup>1</load-on-startup>
    
    </servlet>
    
    <servlet-mapping>
    
    <servlet-name>springDispatcherServlet</servlet-name>
    
    <url-pattern>/</url-pattern>
    
    </servlet-mapping>
    
    
    

    Spring配置

    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
    
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    
    xsi:schemaLocation="http://www.springframework.org/schema/mvc 
    <u>http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd</u>
    
    <u>http://www.springframework.org/schema/beans</u> 
    <u>http://www.springframework.org/schema/beans/spring-beans.xsd</u>
    
    <u>http://www.springframework.org/schema/context</u> 
    <u>http://www.springframework.org/schema/context/spring-context-4.3.xsd</u>">
    
    <context:component-scan base-package="com.atguigu.security"></context:component-scan>
    
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    
    <property name="prefix" value="/WEB-INF/views/"></property>
    
    <property name="suffix" value=".jsp"></property>
    
    </bean>
    
    <mvc:annotation-driven />
    
    <mvc:default-servlet-handler />
    
    </beans>
    

    引入SpringSecurity框架

    添加security-pom依赖

    <dependency>
    
    <groupId>org.springframework.security</groupId>
    
    <artifactId>spring-security-web</artifactId>
    
    <version>4.2.10.RELEASE</version>
    
    </dependency>
    
    <dependency>
    
    <groupId>org.springframework.security</groupId>
    
    <artifactId>spring-security-config</artifactId>
    
    <version>4.2.10.RELEASE</version>
    
    </dependency>
    
    <!-- 标签库 -->
    
    <dependency>
    
    <groupId>org.springframework.security</groupId>
    
    <artifactId>spring-security-taglibs</artifactId>
    
    <version>4.2.10.RELEASE</version>
    
    </dependency>
    

    web.xml中添加SpringSecurity的Filter进行安全控制

    <filter>
    
    <filter-name>springSecurityFilterChain</filter-name><!--名称固定,不能变-->
    
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    
    </filter>
    
    <filter-mapping>
    
    <filter-name>springSecurityFilterChain</filter-name>
    
    <url-pattern>/*</url-pattern>
    
    </filter-mapping>
    

    加入SpringSecurity配置类

    @Configuration、@Bean 注解作用
            
        @Configuration
        @EnableWebSecurity
    
        public class AppWebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        }
    

    SpringSecurity-实验

    授权首页和静态资源

    配置类(AppWebSecurityConfig extends WebSecurityConfigurerAdapter)
    重写configure(HttpSecurity http)方法

    @Configuration
    
    @EnableWebSecurity
    
    public class AppWebSecurityConfig extends WebSecurityConfigurerAdapter { 
    
    @Override
    
    protected void configure(HttpSecurity http) throws Exception {
    
    //super.configure(http); //取消默认配置 
    
    http.authorizeRequests()
    
    .antMatchers("/layui/**","/index.jsp").permitAll() //设置匹配的资源放行
    
    .anyRequest().authenticated(); //剩余任何资源必须认证
    
    }
    
    }
    

    静态资源和index.jsp都可以访问
    不存在的资源
    有权限无资源404

    默认及自定义登录页

    开启formLogin()功能  默认主页时
    静态资源和index.jsp都可以访问

    自定义表单登录逻辑分析

    表单提交地址:${PATH }/index.jsp
    表单提交请求方式:post
    表单提交请求失败,提取错误消息:${SPRING_SECURITY_LAST_EXCEPTION.message}

    自定义认证用户信息

    auth.inMemoryAuthentication()
    
    .withUser("zhangsan").password("123456").roles("ADMIN")
    
    .and()
    
    .withUser("lisi").password("123123").authorities("USER","MANAGER");
    
    


    用户注销完成

    添加注销功能(logout)http.logout()默认规则         
    /logout:退出系统
    如果csrf开启,必须post方式的/logout请求,表单中需要增加csrf token
    logoutUrl();退出系统需要发送的请求
    logoutSuccessUrl();退出系统成功以后要跳转的页面地址
    addLogoutHandler():自定义注销处理器
    deleteCookies():指定需要删除的cookie
    invalidateHttpSession():session失效(DEBUG)

    基于角色的访问控制

    //设置资源可以访问的角色
    http.authorizeRequests().antMatchers("/layui/**","/index.jsp").permitAll() //允许所有人都访问静态资源,无论登录(认证)与否
    
    .antMatchers("/level1/**").hasRole("学徒")
    
    .antMatchers("/level2/**").hasRole("大师")
    
    .antMatchers("/level3/**").hasRole("宗师")
    
    .anyRequest().authenticated(); //放置最后,以上没有规定的都需要权限认证。
    
    //设置拥有该角色的资源可以访问
    
    auth.inMemoryAuthentication()
    
    .withUser("zhangsan").password("123456").roles("ADMIN","学徒","宗师")
    
    .and()
    
    .withUser("自定义访问拒绝处理页面,lisi").password("111111").authorities("USER","MANGER");
    

    自定义访问拒绝处理页面

    http.exceptionHandling().accessDeniedPage("/unauth.html");
    
    <%@ page language="java" contentType="text/html; 
    charset=UTF-8"
    
         pageEncoding="UTF-8"%>
    
    <%
    
         pageContext.setAttribute("PATH", 
    request.getContextPath());
    
    %>
    
    <!DOCTYPE html>
    
    <html>
    
    <head>
    
    <meta charset="utf-8">
    
    <meta name="viewport"
    
         content="width=device-width, initial-scale=1, 
    maximum-scale=1">
    
    <title>武林秘籍管理系统</title>
    
    <link rel="stylesheet" href="${PATH 
    }/layui/css/layui.css">
    
    </head> 
    
    <body class="layui-layout-body">
    
         <div class="layui-layout layui-layout-admin">
    
              <!-- 顶部导航 -->
    
              <%@include file="/WEB-INF/include/navbar.jsp" %>
    
              
    
              <!-- 侧边栏 -->
    
              <%@include file="/WEB-INF/include/sidebar.jsp" 
    %>
    
              
    
              
    
              <div class="layui-body">
    
                  <!-- 内容主体区域 -->
    
                  <div style="padding: 15px;">
    
                       <h1>你无权访问</h1>
    
                  </div>
    
              </div>
    
              <div class="layui-footer"></div>
    
         </div>
    
         <script src="${PATH }/layui/layui.js"></script>
    
         <script src="${PATH 
    }/layui/jquery-2.1.1.min.js"></script>
    
         <script>
    
              //JavaScript代码区域
    
              layui.use('element', function() {
    
                  var element = layui.element;
    
    
    
              });
    
         </script>
    
    </body>
    
    </html>
    
    
    

    http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
    
    @Override
    
    public void handle(HttpServletRequest request, HttpServletResponse response,
    
    AccessDeniedException accessDeniedException) throws IOException, ServletException {
    
    request.setAttribute("message", accessDeniedException.getMessage());
    
    request.getRequestDispatcher("/WEB-INF/views/unauth.jsp").forward(request, response);
    
    }
    
    });
    

    记住我功能

    简单版本

    http.rememberMe();
    

    数据库版本

    • 引入pom.xml包
    
        
            
        
        
        <dependency>
        <groupId>org.springframework</groupId>
    
        <artifactId>spring-orm</artifactId>
    
        <version>4.3.20.RELEASE</version>
    
        </dependency>
    
        <dependency>
    
        <groupId>com.alibaba</groupId>
    
        <artifactId>druid</artifactId>
    
        <version>1.1.12</version>
    
        </dependency>
    
        <!-- mysql驱动 -->
    
        <dependency>
    
            <groupId>mysql</groupId>
    
            <artifactId>mysql-connector-java</artifactId>
    
            <version>5.1.47</version>
    
        </dependency>
    
    • 配置数据源
    <!-- 配置数据源 -->
    
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    
    <property name="username" value="root"></property>
    
    <property name="password" value="root"></property>
    
    <property name="url" value="jdbc:mysql://192.168.137.3:3306/security?useSSL=false"></property>
    
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    
    </bean> 
    
    <!--  jdbcTemplate-->
    
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    
    <property name="dataSource" ref="dataSource"></property>
    
    </bean>
    
    
    
    • 创建表

    • 设置记住我

    <!-- 配置数据源 -->
    
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    
    <property name="username" value="root"></property>
    
    <property name="password" value="root"></property>
    
    <property name="url" value="jdbc:mysql://192.168.137.3:3306/security?useSSL=false"></property>
    
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    
    </bean> 
    
    <!--  jdbcTemplate-->
    
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    
    <property name="dataSource" ref="dataSource"></property>
    
    </bean>
    
    
    

    认证

    自定义UserDetailsService检索用户

    package com.atguigu.security.component;
    
    import java.util.HashSet;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.ColumnMapRowMapper;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.stereotype.Component;
    
    import com.alibaba.druid.util.StringUtils;
    
    @Component
    public class UserDetailsServiceImpl implements UserDetailsService {
    
    	@Autowired
    	JdbcTemplate jdbcTemplate;
    	
    	@Override
    	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    		String queryUser = "SELECT * FROM `t_admin` WHERE loginacct=?";
    		
    		//1、查询指定用户的信息
    		Map<String, Object> map = jdbcTemplate.queryForMap(queryUser, username);
    	
    		//查询用户拥有的角色集合
    		String sql1="SELECT t_role.* FROM t_role LEFT JOIN t_admin_role ON t_admin_role.roleid=t_role.id WHERE t_admin_role.adminid=?";                
    		List<Map<String, Object>> roleList = jdbcTemplate.query(sql1, new ColumnMapRowMapper(), map.get("id"));
    		System.out.println("roleList="+roleList); 
    		
    		//查询用户拥有的权限集合
    		String sql2 = "SELECT distinct t_permission.* FROM t_permission LEFT JOIN t_role_permission ON t_role_permission.permissionid = t_permission.id LEFT JOIN t_admin_role ON t_admin_role.roleid=t_role_permission.roleid WHERE t_admin_role.adminid=?";
    		List<Map<String, Object>> permissionList = jdbcTemplate.query(sql2, new ColumnMapRowMapper(), map.get("id"));
    		 
    		System.out.println("permissionList="+permissionList);
    		
    		//用户权限=【角色+权限】
    		Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
    		                
    		for (Map<String, Object> rolemap : roleList) {
    		String rolename = rolemap.get("name").toString();
    		authorities.add(new SimpleGrantedAuthority("ROLE_"+rolename));
    		}   
    		
    		
    		for (Map<String, Object> permissionmap : permissionList) {
    			String permissionName = permissionmap.get("name").toString();
    			if(!StringUtils.isEmpty(permissionName)) {
    			authorities.add(new SimpleGrantedAuthority(permissionName));
    			}                        
    		}
    		
    		System.out.println("authorities="+authorities); 
    		
    		//return new User(map.get("loginacct").toString(),map.get("userpswd").toString(),
    		//AuthorityUtils.createAuthorityList("ADMIN","USER"));
    		return new User(map.get("loginacct").toString(),map.get("userpswd").toString(),authorities);
    		
    	}
    

    自定义实现类---》Dao层(调用实现类)--》数据库查询的封装到Userdetails对象中
    页面提交的账号密码封装到Authentication对象中
    将密码加密后与数据库中密码进行比对

    基于数据库(MD5密码)认证 (debug)

    配置configure

        //测试:分析源码(验证密码不一致)
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    

    引入MD5加密工具
    PasswordEncoder接口实现类

    package com.atguigu.security.component;
    
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.stereotype.Component;
    
    @Component
    public class PasswordEncoderImpl implements PasswordEncoder {
    
    	@Override
    	public String encode(CharSequence rawPassword) {
    		// TODO Auto-generated method stub
    		return MD5Util.digest(rawPassword.toString());
    	}
    
    	@Override
    	public boolean matches(CharSequence rawPassword, String encodedPassword) {
    		// TODO Auto-generated method stub
    		return encodedPassword.equals(MD5Util.digest(rawPassword.toString()));
    	}
    
    }
    



    基于数据库(BCryptPasswordEncoder)密码加密认证

    细粒度权限控制

    前置细节【Role和Authority的区别】

    用户拥有的权限表示

    roles("ADMIN","学徒","宗师") 
    authorities("USER","MANAGER");
    

    给资源授予权限(角色或权限)

    //.antMatchers("/level1/**").hasRole("学徒")
    
    //.antMatchers("/level1/**").hasAnyRole("学徒","ADMIN")//拥有任何一个角色都可以访问
    
    .antMatchers("/level1/**").hasAnyAuthority("学徒","ADMIN") //拥有任何一个权限都可以访问
    
    .antMatchers("/level2/**").hasRole("大师")
    
    .antMatchers("/level3/**").hasRole("宗师")
    
    roles("ADMIN","学徒","宗师") 
    
    增加"ROLE_"前缀存放:【"ROLE_ADMIN","ROLE_学徒","ROLE_宗师"】
    
    表示拥有的权限。一个角色表示的是多个权限
    
    用户传入的角色不能以ROLE_开头,否则会报错。ROLE_是自动加上的
    
    如果我们保存的用户的角色:直接传入角色的名字,权限【new 
    SimpleGrantedAuthority("ROLE_" + role)】保存即可
    
    
    
    authorities("USER","MANAGER");
    原样存放:【"USER","MANAGER"】
    
    表示拥有的权限。
    
    如果我们保存的是真正的权限;直接传入权限名字,权限【new SimpleGrantedAuthority(role)】保存
    
    无论是Role还是Authority都保存在  List<GrantedAuthority>,每个用户都拥有自己的权限集合->List<GrantedAuthority>
    
    
    

    细粒度的资源控制

    authenticated():通过认证的用户都可以访问
    permitAll():允许所有人访问,即使未登录
    authorizeRequests():更细粒度的控制
    access(String): SpEL:Spring表达式
    .access("hasRole('大师') AND hasAuthority('user:delete') OR hasIpAddress('192.168.50.15')")

    细粒度的资源控制相应注解

    @EnableWebSecurity:开启 Spring Security 注解
    
    @EnableGlobalMethodSecurity(prePostEnabled=true):开启全局的细粒度方法级别权限控制功能
    
    
    
    @PreAuthorize("hasRole('ADMIN')")  
    
    public void addUser(User user){  
    
        //如果具有ROLE_ADMIN 权限 则访问该方法  
    
        ....  
    
    }
    
    
    @PostAuthorize:允许方法调用,但是,如果表达式结果为false抛出异常  
        //returnObject可以获取返回对象user,判断user属性username是否和访问该方法的用户对象的用户名一样。不一样则抛出异常。  
    
        @PostAuthorize("returnObject.user.username==principal.username")  
    
        public User getUser(int userId){  
    
           //允许进入
    
        ...  
    
            return user;
    
        }
    
        
    
    //将结果过滤,即选出性别为男的用户  
    
    @PostFilter("returnObject.user.sex=='男' ")  
    
    public List<User> getUserList(){  
    
        //允许进入
    
        ...  
    
        return user; 
    
    }
    
    
    @PreFilter:允许方法调用,但必须在进入方法前过滤输入值
    
    
    @Secured('ADMIN')   等价于    @PreAuthorize("hasRole('ADMIN')") 
    
    
    

    细粒度权限控制实现步骤

    https://docs.spring.io/spring-security/site/docs/4.0.1.RELEASE/reference/htmlsingle/#el-common-built-in
    开启全局的细粒度方法级别权限控制功能

    @EnableGlobalMethodSecurity(prePostEnabled = true)
        @EnableWebSecurity
    
        @Configuration
    
        public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
    
        }
    
    

    将手动授权的方式注释掉

    //.antMatchers("/level1/**").hasRole("学徒")
    
    //.antMatchers("/level1/**").hasAnyRole("学徒","ADMIN")
    
    //.antMatchers("/level1/**").hasAnyAuthority("学徒","ADMIN")
    
    //.antMatchers("/level1/**").hasAuthority("学徒")
    
    //.antMatchers("/level2/**").hasRole("大师")
    
    //.antMatchers("/level3/**").hasRole("宗师")  
    
    
    

    给访问资源的方法增加注解,进行访问授权

    package com.atguigu.security.controller;
    
    import org.springframework.security.access.prepost.PreAuthorize;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    
    @Controller
    public class GongfuController {
    	
    	@PreAuthorize("hasRole('学徒') AND hasAuthority('luohan')")
    	@GetMapping("/level1/1")
    	public String leve1Page(){
    		return "/level1/1";
    	}
    	
    	@PreAuthorize("hasRole('学徒') AND hasAuthority('wudang')")
    	@GetMapping("/level1/2")
    	public String leve2Page(){
    		return "/level1/2";
    	}
    	
    	@PreAuthorize("hasRole('学徒') AND hasAuthority('quanzhen')")
    	@GetMapping("/level1/3")
    	public String leve3Page(){
    		return "/level1/3";
    	}
    
    }
    
    

    通过数据库加载用户权限

    package com.atguigu.security.component;
    
    import java.util.HashSet;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.ColumnMapRowMapper;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.stereotype.Component;
    
    import com.alibaba.druid.util.StringUtils;
    
    @Component
    public class UserDetailsServiceImpl implements UserDetailsService {
    
    	@Autowired
    	JdbcTemplate jdbcTemplate;
    	
    	@Override
    	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    		String queryUser = "SELECT * FROM `t_admin` WHERE loginacct=?";
    		
    		//1、查询指定用户的信息
    		Map<String, Object> map = jdbcTemplate.queryForMap(queryUser, username);
    	
    		//查询用户拥有的角色集合
    		String sql1="SELECT t_role.* FROM t_role LEFT JOIN t_admin_role ON t_admin_role.roleid=t_role.id WHERE t_admin_role.adminid=?";                
    		List<Map<String, Object>> roleList = jdbcTemplate.query(sql1, new ColumnMapRowMapper(), map.get("id"));
    		System.out.println("roleList="+roleList); 
    		
    		//查询用户拥有的权限集合
    		String sql2 = "SELECT distinct t_permission.* FROM t_permission LEFT JOIN t_role_permission ON t_role_permission.permissionid = t_permission.id LEFT JOIN t_admin_role ON t_admin_role.roleid=t_role_permission.roleid WHERE t_admin_role.adminid=?";
    		List<Map<String, Object>> permissionList = jdbcTemplate.query(sql2, new ColumnMapRowMapper(), map.get("id"));
    		 
    		System.out.println("permissionList="+permissionList);
    		
    		//用户权限=【角色+权限】
    		Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
    		                
    		for (Map<String, Object> rolemap : roleList) {
    		String rolename = rolemap.get("name").toString();
    		authorities.add(new SimpleGrantedAuthority("ROLE_"+rolename));
    		}   
    		
    		
    		for (Map<String, Object> permissionmap : permissionList) {
    			String permissionName = permissionmap.get("name").toString();
    			if(!StringUtils.isEmpty(permissionName)) {
    			authorities.add(new SimpleGrantedAuthority(permissionName));
    			}                        
    		}
    		
    		System.out.println("authorities="+authorities); 
    		
    		//return new User(map.get("loginacct").toString(),map.get("userpswd").toString(),
    		//AuthorityUtils.createAuthorityList("ADMIN","USER"));
    		return new User(map.get("loginacct").toString(),map.get("userpswd").toString(),authorities);
    		
    	}
    }
    
    

    准备数据

    查询用户拥有的角色集合 
        SELECT 
          t_role.* 
    
        FROM
    
          t_role 
    
          LEFT JOIN t_admin_role 
    
            ON t_admin_role.roleid = t_role.id 
    
        WHERE t_admin_role.userid = 1
    
    
        查询用户拥有的权限集合
        SELECT DISTINCT 
          t_permission.* 
    
        FROM
    
          t_permission 
    
          LEFT JOIN t_role_permission 
    
            ON t_role_permission.permissionid = 
        t_permission.id 
    
          LEFT JOIN t_admin_role 
    
            ON t_admin_role.roleid = 
        t_role_permission.roleid 
    
        WHERE t_admin_role.userid = 1
    

    SpringSecurity-原理

    初始化方法

    过滤器:功能扩展的多个过滤器->责任链设计模式

    获取过滤器链中的过滤器,封装为虚拟的VirtualFilterChain对象,并开始执行过滤

    开始一个一个的执行过滤器

    不同过滤器介绍(直接看文档)

    UsernamePasswordAuthenticationFilter认证原理

    执行过滤器,获取到页面的用户名和密码

    将username和password包装成UsernamePasswordAuthenticationToken

    获取系统的认证管理器(AuthenticationManager)来调用authenticate方法完成认证(this.getAuthenticationManager().authenticate(authRequest))
    AuthenticationManager获取ProviderManager来调用ProviderManager.authenticate()

    ProviderManager获取到所有的AuthenticationProvider判断当前的提供者能否支持,如果支持provider.authenticate(authentication);
    DaoAuthenticationProvider( authentication :页面封装用户名和密码的对象)

    3.2.1)、retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);

    3.2.1.1)、 loadedUser = this.getUserDetailsService().loadUserByUsername(username);

    (调用我们自己的UserDetailsService来去数据库查用户,按照用户名查出来的用户的详细信息)封装成UserDetail

    3.2.2)、 preAuthenticationChecks.check(user);(预检查,账号是否被锁定等…)

    3.2.3)、 additionalAuthenticationChecks(附加的认证检查)

    3.2.3.1)、使用passwordEncoder. matches检查页面的密码和数据库的密码是否一致

    3.2.4)、 postAuthenticationChecks.check(user);(后置认证,检查密码是否过期)

    3.2.5)、 createSuccessAuthentication:将认证成功的信息重新封装成UsernamePasswordAuthenticationToken

    3.3)、 3.2又返回了一个新的UsernamePasswordAuthenticationToken,然后擦掉密码

    eventPublisher.publishAuthenticationSuccess(result);认证成功


    successfulAuthentication(request, response, chain, authResult);
    通过调用 SecurityContextHolder.getContext().setAuthentication(...)  将 Authentication 对象赋给当前的 SecurityContext

    认证流程

    
    
    用户使用用户名和密码登录
    
    
    
    用户名密码被过滤器(默认为 UsernamePasswordAuthenticationFilter)获取到,封装成 
    Authentication(UsernamePasswordAuthenticationToken)
    
    
    
    token(Authentication实现类)传递给 AuthenticationManager 进行认证
    
    
    
    AuthenticationManager 认证成功后返回一个封装了用户权限信息的 Authentication 对象
    
    
    
    通过调用 SecurityContextHolder.getContext().setAuthentication(...)  将 Authentication 对象赋给当前的 SecurityContext 
    
    
    
    将用户的信息保存到当前线程上,共享起来
    
    
    
    SecurityContextHolder.getContext();就能获取到之前认证好的Authentication对象(UsernamePasswordAuthenticationToken
    
    
    
  • 相关阅读:
    MOCK服务小结
    微信收款「商业版」与「个人版」有什么区别?看完这篇你就懂!
    LB 负载均衡的层次结构
    中兴应用之星携手天翼开放平台:让APP开发更简单
    常见面试题之二叉树
    【深入JAVA EE】Spring配置文件解析
    spring-struts-mybatis整合错误集锦
    Redis安装
    Atitit.ati&#160;dwr的原理and设计&#160;attilax&#160;总结&#160;java&#160;php&#160;版本号
    Android UI开发神兵利器之Icon
  • 原文地址:https://www.cnblogs.com/suit000001/p/13848830.html
Copyright © 2020-2023  润新知