五分钟带你玩转SpringSecurity(四)配置全解析,带你掌握security核心要点

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 五分钟带你玩转SpringSecurity(四)配置全解析,带你掌握security核心要点


1认证

认证:辨别用户是否本系统用户。

优势:1 提供多样式的加密方法

          2 提供多样式的用户存储方式

          3 使用者无需关注验证封装业务 只需要提供查询方法即可

          4 多样式的认证方式

          5 提供用户信息获取方式

可扩展的功能

     1 记住我

     2 邮箱验证

     3 手机验证

     4 验证码验证

配置详解

spring security统一实现WebSecurityConfigurerAdapter接口 按照以下需求添加以下配置 就可以整合成功

@Configuration
@EnableWebSecurity // 开启springsecurity过滤链 filter
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启注解方法级别权限控制
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    //实现输入是明文 存储到数据库为密文 写死即可
    @Bean
    public PasswordEncoder passwordEncoder() {
        // 明文+随机盐值》加密存储
        return new BCryptPasswordEncoder();
    }
    //用户验证的业务流程 也就是查询用户的业务代码
    @Autowired
    UserDetailsService customUserDetailsService;
    /**
     * 认证管理器: 将上文查询用户是否存在的service按样式写入 修改service 其余写死 还有密码写死或存放内存中等方式 这里不讨论
     * 1. 认证信息(用户名,密码)
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 数据库存储的密码必须是加密后的也就是必须要有passwordEncoder,不然会报错:There is no PasswordEncoder mapped for
        auth.userDetailsService(customUserDetailsService);
    }
    //验证码配置
    @Autowired
    private ImageCodeValidateFilter imageCodeValidateFilter;
    //当验证成功后可以返回json或者路径 但是现在基于前后台分离 大多数都是返回json AuthenticationSuccessHandler为成功后转为json的处理 按照本文配置即可
    @Autowired
    private AuthenticationSuccessHandler customAuthenticationSuccessHandler;
    //当验证成功后可以返回json或者路径 但是现在基于前后台分离 大多数都是返回json AuthenticationFailureHandler为失败后转为json的处理 按照本文配置即可
    @Autowired
    private AuthenticationFailureHandler customAuthenticationFailureHandler;
    //建立数据源
    @Autowired
    DataSource dataSource;
    @Autowired
    private InvalidSessionStrategy invalidSessionStrategy;
    /**
     * 当同个用户session数量超过指定值之后 ,会调用这个实现类
     */
    @Autowired
    private SessionInformationExpiredStrategy sessionInformationExpiredStrategy;
    /**
     * 持久化token
     * @return
     */
    @Bean
    public JdbcTokenRepositoryImpl jdbcTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        // 是否启动项目时自动创建表,true自动创建
//        jdbcTokenRepository.setCreateTableOnStartup(true);
        return jdbcTokenRepository;
    }
    /**
     * 核心拦截器 当你认证成功之后 ,springsecurity它会重写向到你上一次请求上
     * 资源权限配置:
     * 1. 被拦截的资源
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //调用验证码过滤器 下文会详细介绍
        http.addFilterBefore(imageCodeValidateFilter, UsernamePasswordAuthenticationFilter.class)
                .formLogin() // 表单登录方式
                .loginPage("/login/page") //登录页的页面地址
                .loginProcessingUrl("/login/form") // 登录表单提交处理url, 默认是/login
                .usernameParameter("username") //默认的是 username
                .passwordParameter("password")  // 默认的是 password
                .successHandler(customAuthenticationSuccessHandler) //登录成功返回的json
                .failureHandler(customAuthenticationFailureHandler) //登录失败返回的json
                .and() //每个类型的配置 以.and()间隔 相当于;
                .authorizeRequests() // 授权请求
                .antMatchers("/login/page",
                        "/code/image","/mobile/page", "/code/mobile",
                        "/code/image",
                        "/code/mobile",
                        "/mobile/page"
                ).permitAll() // 放行/login/page不需要认证可访问 因为如果在调用验证接口时还需要权限 那么就没有入口了 所以一些不需要登录就能访问的接口在此配置
                // 此处是鉴权
                // 有 sys:user 权限的可以访问任意请求方式的/role
                .antMatchers("/user").hasAuthority("sys:user")
                // 有 sys:role 权限的可以访问 get方式的/role
                .antMatchers(HttpMethod.GET,"/role").hasAuthority("sys:role")
                .antMatchers(HttpMethod.GET, "/permission")
                // ADMIN 注意角色会在前面加上前缀 ROLE_ , 也就是完整的是 ROLE_ADMIN, ROLE_ROOT
                .access("hasAuthority('sys:premission') or hasAnyRole('ADMIN', 'ROOT')")
                // 此处是鉴权
                .anyRequest().authenticated() //所有访问该应用的http请求都要通过身份认证才可以访问
                .and()
                .rememberMe() //记住我功能
                 //记住功能配置
                .tokenRepository(jdbcTokenRepository()) //保存token信息
                .tokenValiditySeconds(604800) //记住我有效时长
                .and()
                .sessionManagement()// session管理
                .invalidSessionStrategy(invalidSessionStrategy) //当session失效后的处理类 //.expiredSessionStrategy(sessionInformationExpiredStrategy)// 当用户达到最大session数后,则调用此处的实现
                .maximumSessions(1) // 每个用户在系统中最多可以有多少个session
                .maxSessionsPreventsLogin(true) // 当一个用户达到最大session数,则不允许后面再登录
                .sessionRegistry(sessionRegistry())
                .and().and()
                .logout()//登出相关
                .addLogoutHandler(customLogoutHandler) // 退出清除缓存
                .logoutUrl("/user/logout") // 退出请求路径
                .logoutSuccessUrl("/mobile/page") //退出成功后跳转地址
                .deleteCookies("JSESSIONID") // 退出后删除什么cookie值
        ;// 注意不要少了分号
        http.csrf().disable(); // 关闭跨站请求伪造
    }
    /**
     * 退出清除缓存
     */
    @Autowired
    private CustomLogoutHandler customLogoutHandler;
    /**
     * 为了解决退出重新登录问题
     * @return
     */
    @Bean
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }
    /**
     * 一般是针对静态资源放行
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web){
        web.ignoring().antMatchers("/js/**", "/css/**");
    }
}

清除缓存方法

@Component
public class CustomLogoutHandler implements LogoutHandler {
    @Autowired
    private SessionRegistry sessionRegistry;
    @Override
    public void logout(HttpServletRequest request,
                       HttpServletResponse response,
                       Authentication authentication) {
        // 退出之后 ,将对应session从缓存中清除 SessionRegistryImpl.principals
        sessionRegistry.removeSessionInformation(request.getSession().getId());
    }
}

编写user验证方法类

需要继承UserDetailsService  为jar包提供

1. public interface UserDetailsService {
2.     UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
3. }

获取SysUser实体方法和获取此人菜单的方法 这里不多说

public abstract class AbstractUserDetailsService implements UserDetailsService {
    @Autowired
    private SysPermissionService sysPermissionService;
    /**
     * 这个方法交给子类去实现它,查询用户信息
     *
     * @param usernameOrMobile 用户名或者手机号
     * @return
     */
    public abstract SysUser findSysUser(String usernameOrMobile);
    @Override
    public UserDetails loadUserByUsername(String usernameOrMobile) throws UsernameNotFoundException {
        // 1. 通过请求的用户名去数据库中查询用户信息
        SysUser sysUser = findSysUser(usernameOrMobile);
        // 通过用户id去获取权限信息
        findSysPermission(sysUser);
        return sysUser;
    }
    private void findSysPermission(SysUser sysUser) {
        if (sysUser == null) {
            throw new UsernameNotFoundException("用户名或密码错误");
        }
        // 2. 查询该用户有哪一些权限
        List<SysPermission> permissions = sysPermissionService.findByUserId(sysUser.getId());
        if (CollectionUtils.isEmpty(permissions)) {
            return;
        }
        // 在左侧菜单 动态渲染会使用,目前先把它都传入
        sysUser.setPermissions(permissions);
        // 3. 封装权限信息
        List<GrantedAuthority> authorities = Lists.newArrayList();
        for (SysPermission sp : permissions) {
            // 权限标识
            String code = sp.getCode();
            authorities.add(new SimpleGrantedAuthority(code));
        }
        sysUser.setAuthorities(authorities);
    }
}
@Component("customUserDetailsService")
public class CustomUserDetailsService extends AbstractUserDetailsService{
    Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired // 不能删掉,不然报错
    PasswordEncoder passwordEncoder;
    @Autowired
    SysUserService sysUserService;
    @Override
    public SysUser findSysUser(String usernameOrMobile) {
        logger.info("请求认证的用户名: " + usernameOrMobile);
        // 1. 通过请求的用户名去数据库中查询用户信息
        return sysUserService.findByUsername(usernameOrMobile);
    }
}
CustomAuthenticationSuccessHandler与CustomAuthenticationFailureHandler
//此类没有注入,因为楼主并没有使用
@Component("customAuthenticationSuccessHandler")
public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
    @Autowired(required = false) // 容器中可以不需要有接口的实现,如果有则自动注入
    AuthenticationSuccessListener authenticationSuccessListener;
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
        HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        if(authenticationSuccessListener != null) {
            // 当认证之后 ,调用此监听,进行后续处理,比如加载用户权限菜单
            authenticationSuccessListener.successListener(request, response, authentication);
        }
        if(LoginResponseType.JSON.equals(
                    "post")) {
            // 认证成功后,响应JSON字符串
            MengxueguResult result = MengxueguResult.ok("认证成功");
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(result.toJsonString());
        }else {
            //重定向到上次请求的地址上,引发跳转到认证页面的地址
            logger.info("authentication: " + JSON.toJSONString(authentication));
            super.onAuthenticationSuccess(request, response, authentication);
        }
    }
}
@Component("customAuthenticationFailureHandler")
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
    /**
     *
     * @param exception 认证失败时抛出异常
     */
    @Override
    public void onAuthenticationFailure(HttpServletRequest request,
            HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        if(LoginResponseType.JSON.equals("post")) {
            // 认证失败响应JSON字符串,
            MengxueguResult result = MengxueguResult.build(HttpStatus.UNAUTHORIZED.value(), exception.getMessage());
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(result.toJsonString());
        }else {
            // 重写向回认证页面,注意加上 ?error
//            super.setDefaultFailureUrl(securityProperties.getAuthentication().getLoginPage()+"?error");
            // 获取上一次请求路径
            String referer = request.getHeader("Referer");
            logger.info("referer:" + referer);
            // 如果下面有值,则认为是多端登录,直接返回一个登录地址
            Object toAuthentication = request.getAttribute("toAuthentication");
            String lastUrl = toAuthentication != null ? "/login/page"
                    : StringUtils.substringBefore(referer,"?");
            logger.info("上一次请求的路径 :" + lastUrl);
            super.setDefaultFailureUrl(lastUrl+"?error");
            super.onAuthenticationFailure(request, response, exception);
        }
    }
}


相关文章
|
2月前
|
域名解析 存储 缓存
DNS是什么?内网电脑需要配置吗?
【10月更文挑战第22天】DNS是什么?内网电脑需要配置吗?
457 1
|
3月前
|
机器学习/深度学习 调度
mmseg配置解析 Polynomial Decay 多项式衰减
Polynomial Decay(多项式衰减)是一种常用的学习率调度方法,通过多项式函数逐步减少学习率,帮助模型更好地收敛。公式为:\[ lr = (lr_{initial} - \eta_{min}) \times \left(1 - \frac{current\_iter}{max\_iters}\right)^{power} + \eta_{min} \]。参数包括初始学习率、最小学习率、当前迭代次数、总迭代次数和衰减指数。适用于需要平滑降低学习率的场景,特别在训练后期微调模型参数。
96 0
mmseg配置解析 Polynomial Decay 多项式衰减
|
3月前
|
网络协议 Docker 容器
docker中的DNS配置
【10月更文挑战第5天】
743 1
|
3月前
|
弹性计算 网络协议 Ubuntu
如何在阿里云国际版Linux云服务器中自定义配置DNS
如何在阿里云国际版Linux云服务器中自定义配置DNS
|
3月前
|
JSON JavaScript 前端开发
深入解析ESLint配置:从入门到精通的全方位指南,精细调优你的代码质量保障工具
深入解析ESLint配置:从入门到精通的全方位指南,精细调优你的代码质量保障工具
133 0
|
3月前
|
编解码 计算机视觉
mmseg配置解析 align_corners=False
`align_corners=False` 是图像插值操作中的一个参数,影响输入和输出图像的角点对齐方式。`align_corners=True` 严格对齐角点,而 `align_corners=False` 均匀分布像素点,更适用于保持整体比例关系的任务,如语义分割。
66 0
|
3月前
|
机器学习/深度学习 编解码
mmseg配置解析 contract_dilation=True
`contract_dilation=True` 是 ResNetV1c 中的一种设置,用于解决多层膨胀卷积中的“栅格效应”。通过调整膨胀率,使卷积核在不同阶段更密集地覆盖输入特征图,避免信息丢失,提升特征提取质量,尤其在语义分割任务中效果显著。
60 0
|
3月前
|
XML Java 数据格式
手动开发-简单的Spring基于注解配置的程序--源码解析
手动开发-简单的Spring基于注解配置的程序--源码解析
59 0
|
3月前
|
XML Java 数据格式
手动开发-简单的Spring基于XML配置的程序--源码解析
手动开发-简单的Spring基于XML配置的程序--源码解析
96 0
|
5月前
|
持续交付 jenkins Devops
WPF与DevOps的完美邂逅:从Jenkins配置到自动化部署,全流程解析持续集成与持续交付的最佳实践
【8月更文挑战第31天】WPF与DevOps的结合开启了软件生命周期管理的新篇章。通过Jenkins等CI/CD工具,实现从代码提交到自动构建、测试及部署的全流程自动化。本文详细介绍了如何配置Jenkins来管理WPF项目的构建任务,确保每次代码提交都能触发自动化流程,提升开发效率和代码质量。这一方法不仅简化了开发流程,还加强了团队协作,是WPF开发者拥抱DevOps文化的理想指南。
116 1

热门文章

最新文章

推荐镜像

更多