SpringSecurity原理

一、过滤器

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

SpringSecurity原理

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

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

SpringSecurity原理

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

SpringSecurity原理

二、过滤器的加载过程

1.DelegatingFilterProxy类中String targetBeanName = this.getTargetBeanName()获取到固定值FilterChainProxy,getBean()来获取FilterChainProxy的bean。

SpringSecurity原理

SpringSecurity原理

2.FilterChainProxy类中doFilterInternal方法的List filters = getFilters(firewallRequest)根据当前的请求从 filterChains 中找到对应的过滤器链,然后由该过滤器链去处理请求,FilterChainProxy中getFilters方法中的一个SecurityFilterChain对应多个Filter,SecurityFilterChain中的getFilters()由DefaultSecurityFilterChain类实现getFilters()方法获取所有过滤器

SpringSecurity原理

三、配置

package com.xxmfl.mp.security.config;


import com.xxmfl.framework.security.handle.AuthenticationEntryPointImpl;
import com.xxmfl.mp.security.filter.JwtAuthenticationTokenFilter;
import com.xxmfl.mp.security.handle.LogoutSuccessHandlerImpl;
import com.xxmfl.mp.security.service.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 自定义用户认证逻辑
     */
    @Autowired
    private UserDetailsServiceImpl securityUserDetailsService;

    /**
     * 认证失败处理类
     */
    @Autowired
    private AuthenticationEntryPointImpl unauthorizedHandler;
    /**
     * 退出处理类
     */
    @Autowired
    private LogoutSuccessHandlerImpl logoutSuccessHandler;

    /**
     * token认证过滤器
     */
    @Autowired
    private JwtAuthenticationTokenFilter authenticationTokenFilter;

    /**
     * 跨域过滤器
     */
    @Autowired
    private CorsFilter corsFilter;

    /**
     * 解决 无法直接注入 AuthenticationManager
     *
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception
    {
        return super.authenticationManagerBean();
    }

    /**
     * anyRequest          |   匹配所有请求路径
     * access              |   SpringEl表达式结果为true时可以访问
     * anonymous           |   匿名可以访问
     * denyAll             |   用户不能访问
     * fullyAuthenticated  |   用户完全认证可以访问(非remember-me下自动登录)
     * hasAnyAuthority     |   如果有参数,参数表示权限,则其中任何一个权限可以访问
     * hasAnyRole          |   如果有参数,参数表示角色,则其中任何一个角色可以访问
     * hasAuthority        |   如果有参数,参数表示权限,则其权限可以访问
     * hasIpAddress        |   如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
     * hasRole             |   如果有参数,参数表示角色,则其角色可以访问
     * permitAll           |   用户可以任意访问
     * rememberMe          |   允许通过remember-me登录的用户访问
     * authenticated       |   用户登录后可访问
     */
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception
    {
        httpSecurity
                // CSRF禁用,因为不使用session
                .csrf().disable()
                // 认证失败处理类
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                // 基于token,所以不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                // 过滤请求
                .authorizeRequests()
                // 对于登录login 注册register 验证码captchaImage 允许匿名访问
                .antMatchers(
                        "/login",
                        "/register",
                        "/captchaImage"
                .antMatchers(
                        HttpMethod.GET,
                        "/",
                        "/*.html",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js",
                        "/profile/**"
                ).permitAll()
                .antMatchers("/swagger-ui.html").anonymous()
                .antMatchers("/swagger-resources/**").anonymous()
                .antMatchers("/webjars/**").anonymous()
                .antMatchers("/*/api-docs").anonymous()
                .antMatchers("/druid/**").anonymous()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated()
                .and()
                .headers().frameOptions().disable();
        httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
        // 添加JWT filter
        httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        // 添加CORS filter
        httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
        httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
    }

    /**
     * 强散列哈希加密实现
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder()
    {
        return new BCryptPasswordEncoder();
    }

    /**
     * 身份认证接口
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception
    {
        auth.userDetailsService(securityUserDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }

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

    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }
}
package com.xxmfl.mp.security.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xxmfl.mp.security.LoginUserAccountInfo;
import com.xxmfl.system.domain.UserAccountInfo;
import com.xxmfl.system.mapper.UserAccountInfoMapper;
import org.springframework.beans.factory.annotation.Autowired;
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.Service;

import javax.annotation.PostConstruct;


@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

   @Autowired
   private UserAccountInfoMapper userMapper;

    private  static  UserDetailsServiceImpl userDetailsService;
    @PostConstruct //通过@PostConstruct实现初始化bean之前进行的操作
    public void init() {
        userDetailsService = this;
        userDetailsService.userMapper = this.userMapper;
    }

    @Override
    public UserDetails loadUserByUsername(String openId) throws UsernameNotFoundException {
        QueryWrapper queryWrapper=new QueryWrapper();
        queryWrapper.eq("open_id",openId);
        UserAccountInfo user= userMapper.selectOne(queryWrapper);
        if(user!=null){
           return  new LoginUserAccountInfo(user);
        }
        throw new UsernameNotFoundException("用户名不存在");
    }
}
package com.xxmfl.mp.security;

import com.alibaba.fastjson.annotation.JSONField;
import com.xxmfl.common.core.domain.entity.SysUser;
import com.xxmfl.system.domain.UserAccountInfo;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.Set;

/**
 * 登录用户身份权限
 * 
 * @author xxmfl
 */
@Data
public class LoginUserAccountInfo implements UserDetails
{
    private static final long serialVersionUID = 1L;

    /**
     * 用户ID
     */
    private Integer userId;


    /**
     * 用户唯一标识
     */
    private String token;

    /**
     * 登录时间
     */
    private Long loginTime;

    /**
     * 过期时间
     */
    private Long expireTime;


    /**
     * 权限列表
     */
    private Set permissions;

    /**
     * 用户信息
     */
    private UserAccountInfo userAccountInfo;

     public LoginUserAccountInfo(UserAccountInfo userAccountInfo){
         this.userAccountInfo=userAccountInfo;
     }

    @JSONField(serialize = false)
    @Override
    public String getPassword()
    {
        return null;
    }

    @Override
    public String getUsername()
    {
        return userAccountInfo.getNickName();
    }

    /**
     * 账户是否未过期,过期无法验证
     */
    @JSONField(serialize = false)
    @Override
    public boolean isAccountNonExpired()
    {
        return true;
    }

    /**
     * 指定用户是否解锁,锁定的用户无法进行身份验证
     * 
     * @return
     */
    @JSONField(serialize = false)
    @Override
    public boolean isAccountNonLocked()
    {
        return true;
    }

    /**
     * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
     * 
     * @return
     */
    @JSONField(serialize = false)
    @Override
    public boolean isCredentialsNonExpired()
    {
        return true;
    }

    /**
     * 是否可用 ,禁用的用户不能身份验证
     * 
     * @return
     */
    @JSONField(serialize = false)
    @Override
    public boolean isEnabled()
    {
        return true;
    }


    public LoginUserAccountInfo() {
    }

    public Integer getUserId() {
        return this.userAccountInfo.getId();
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

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

四、认证流程

SpringSecurity原理



发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章