精华内容
下载资源
问答
  • 江南一点雨

    千次阅读 多人点赞 2019-06-28 09:41:03
    精品资源 松哥整理了手上一些视频资源,这些资源很多都是最新的...【江南一点雨】 专注于 SPRING BOOT+微服务以及前后端分离技术,每天推送原创技术干货,关注后回复 JAVA,领取松哥为你精心准备的 JAVA 干货!

    精品资源

    松哥整理了手上一些视频资源,这些资源很多都是最新的视频,但是由于资源数据流巨大,超过 500G ,涉及到 Java 基础、Web 基础、数据库、Java 高级、Java 分布式、微服务、大数据、人工智能、大前端、Python 等,非常丰富,这么多资料如果我分享成一个文件夹,那个链接很容易失效,反而不利于大家以后查看,因此我将资源细分了下,确保每一个分享链接不会轻易失效,这些资源都是长期有效的,大家需要时可以随时来松哥公众号中按需索取,如果有某一个链接失效了,大家也可以在本文末尾留言,我会重新分享。

    资源获取方式:根据下面的索引,大家可以选择自己需要的资源,然后在松哥公众号【牧码小子】后台回复对应的口令,就可以获取到资源的百度云盘下载地址。公众号二维码如下:

    另外本文会定期更新,松哥有新资源的时候会及时分享给大家,欢迎各位小伙伴保持关注。

    Java 基础

    资源名称 口令
    Java 基础语法 javaboy4096
    Java 面向对象 javaboy6148
    JavaSE 飞机大战项目 javaboy2053
    深入面向对象和数组 javaboy8200
    Java 常用类详解 javaboy4105
    Java 异常机制解析 javaboy6157
    Java 集合与数据结构 javaboy2062
    JavaIO 流全解析 javaboy8209
    深入理解 Java 多线程 javaboy4114
    Java 网络编程 javaboy6166
    手动开发一个 Web 服务器 javaboy2071
    深入理解 Java 注解+反射 javaboy8218
    Java23 种设计模式 javaboy4123
    学会 Java 正则表达式 javaboy6175
    JDBC 详解 javaboy2080
    独立开发 SORM 框架 javaboy8227
    快人一步,Java10 新特性全解析 javaboy4132
    Java 数据结构和算法 javaboy6184
    深入理解 Java 虚拟机 javaboy2089
    Java 解析XML文件 javaboy8236

    数据库

    资源名称 口令
    Oracle 数据库安装及简单 SQL javaboy4141
    Oracle 账户管理及查询语句 javaboy6193
    Oracle 中的函数 javaboy2098
    Oracle 中的子查询 javaboy8245
    Oracle 中常见的表操作 javaboy4150
    Oracle 中的数据备份 javaboy6202
    MySQL 基础 javaboy2107
    PowerDesigner 教程 javaboy8254
    JDBC 操作数据库 javaboy4159
    MySQL 优化 javaboy6211
    Oracle 高级课程 javaboy2116
    数据库与 SQL 优化 javaboy6283
    数据库集群与高并发 javaboy2188

    Web 基础

    资源名称 口令
    HTML 入门教程 javaboy8263
    CSS 教程 javaboy4168
    JavaScript 视频教程 javaboy6220
    jQuery 视频教程 javaboy2125
    EasyUI 视频教程 javaboy8272
    Servlet 基础 javaboy4177
    Servlet 中的 Request 和 Response javaboy6229
    Servlet 请求转发与重定向 javaboy2134
    Session 和 Cookie javaboy8281
    JSP 详解 javaboy4186
    用户管理系统实战 javaboy6238
    Ajax 详解 javaboy2143
    EL 和 JSTL javaboy8290
    过滤器详解 javaboy4195
    监听器详解 javaboy6247
    KnockoutJS 实战视频 javaboy2152

    Java 高级

    资源名称 口令
    IntelliJIDEA 视频教程 javaboy4285
    Java 高并发秒杀方案 javaboy8299
    Activiti 工作流实战解析 javaboy4204
    Java 并发编程与高并发实战 javaboy6256
    Linux 快速入门 javaboy2161
    Maven 详解 javaboy8308
    Git 应用详解 javaboy4213
    Svn 入门教程 javaboy6265
    高并发编程与线程池 javaboy2170
    系统优化与 JVM 调优 javaboy8317
    Java 编程规范 javaboy4222
    AIO、BIO、NIO 详解 javaboy6274
    Netty 高级视频教程 javaboy2179
    ActiveMQ 消息中间详解 javaboy8326
    单点登录视频教程 javaboy4231
    Dubbo 详解 javaboy8335
    Redis 全解析 javaboy4240
    VSFTPD+NGINX 视频教程 javaboy6292
    MyBatis 视频教程 javaboy2197
    Spring4 视频教程 javaboy8344
    SpringMVC 视频教程 javaboy4249
    SSM 框架整合视频教程 javaboy6301
    RBAC 权限控制视频教程 javaboy2206
    Hibernate4 视频教程 javaboy8353
    Jfinal 视频教程 javaboy4258
    Shiro 视频教程 javaboy6310
    Solr 视频教程 javaboy2215
    Struts2 视频教程 javaboy8362
    Nginx 视频教程 javaboy4267
    Redis 缓存详解 javaboy6319
    JVM 虚拟机优化 javaboy2224
    Zookeeper 详解视频 javaboy8371
    Linux 基本操作 javaboy6328
    架构师面试攻略(文档) javaboy2233
    架构师面试攻略(视频) javaboy8380
    JUC 视频教程 javaboy6400
    MySQL 高级教程 javaboy2305
    Java 邮件开发教程 javaboy8452
    Maven 实战视频 javaboy8443
    自己 DIY 一个 Tomcat javaboy4339

    大前端

    资源名称 口令
    HTML5 新特性 javaboy4276
    AngularJS 视频教程 javaboy6337
    Grunt 视频教程 javaboy2242
    Gulp 视频教程 javaboy8389
    Webpack 视频教程 javaboy4294
    Bootstrap 视频教程 javaboy6346
    CSS3 视频教程 javaboy2251
    ES6 视频教程 javaboy8398
    HTML5 核心技术 javaboy4303
    HTML5 实战 javaboy6355
    HTML5 项目实战 javaboy2260
    JS 模块化视频教程 javaboy8407
    less 视频教程 javaboy4312
    NodeJS 视频教程 javaboy6364
    React 视频教程 javaboy2269
    Zepto 视频教程 javaboy8416
    HTML+CSS 实战视频 javaboy4321
    JavaScript140 集 javaboy6373
    jQuery 视频教程 javaboy2278
    JavaScript 高级语法视频教程 javaboy8425
    Vue 项目实战视频 javaboy4330
    CSS3 特效实战 javaboy6382
    HTML5 特效实战 javaboy2287
    HTML5+Canvas 实现刮刮卡 javaboy8434
    Gradle 从入门到精通 javaboy6391
    mpvue 项目实战 javaboy2296
    Vue 最新最全视频教程 javaboy4348

    大数据

    资源名称 口令
    Linux 操作系统 javaboy4357
    Linux 基本命令 javaboy6409
    Linux 文件安装 javaboy2314
    Shell 编程 javaboy8461
    网络基础知识 javaboy4366
    LVS 集群与高并发 javaboy6418
    Nginx 和高并发 javaboy2323
    keepalive 和单点故障 javaboy8470
    HDFS 分布式文件系统 javaboy4375
    mapreduce 分布式计算 javaboy6427
    YARN 资源管理与任务调度 javaboy2332
    mapreduce 计算案例 javaboy8479
    HIVE 视频教程 javaboy4384
    Hbase 数据库详解 javaboy6436
    zookeeper 协同处理 javaboy2341
    CDH 使用 javaboy8488
    HUE 使用 javaboy4393
    IMPALA 详解 javaboy6445
    oozie 详解 javaboy2350
    elasticsearch 详解 javaboy8497
    Redis 内存数据 javaboy4402
    Scala 入门 javaboy6454
    Spark 详解 javaboy2359
    Spark 高级 javaboy8506
    Spark-Stream 流式计算 javaboy4411
    Kafka 分布式消息队列 javaboy6463
    STORM 流式计算框架 javaboy2368
    Python 语言基础 javaboy8515
    回归算法 javaboy4420
    分类算法、决策树 javaboy6472
    聚类算法、微博案例 javaboy2377
    推荐算法 javaboy8524
    大型电商日志分析(项目实战) javaboy4429
    智慧交通(项目实战) javaboy6481
    智能 App(项目实战) javaboy2386

    人工智能

    资源名称 口令
    人工智能入门 javaboy8533
    线性回归深入与代码实现 javaboy4438
    梯度下降算发实现 javaboy6490
    逻辑回归详解和应用 javaboy2395
    分类项目案例与神经网络算法 javaboy8542
    多分类、决策树分类与随机森林分类 javaboy4447
    分类评估与聚类 javaboy6499
    密度聚类与谱聚类 javaboy2404
    Tensorflow 安装并实现线性回归 javaboy8551
    TensorFlow 深入、TensorFlow可视化 javaboy4456
    DNN 深度神经网络手写图片识别 javaboy6508
    TensorBoard 可视化 javaboy2413
    卷积神经网络、CNN 识别图片 javaboy8560
    卷积神经网络深入,AlexNet 模型实现 javaboy4465
    Keras 深度学习框架 javaboy6517

    总结

    资源还是不错的,松哥也是费了很大功夫才整理好的,希望对大家的技能提升有所帮助。

    #学习资源

    喜欢这篇文章吗?扫码关注公众号【江南一点雨】【江南一点雨】专注于 SPRING BOOT+微服务以及前后端分离技术,每天推送原创技术干货,关注后回复 JAVA,领取松哥为你精心准备的 JAVA 干货!

    展开全文
  • Security的玩法是跟着江南一点雨学习的。 本文是跟着江南一点雨学习的学习笔记! 1.自定义认证逻辑 不破坏原有的过滤器链,又实现了自定义认证功能(基于Session,不是JSON交互) (1)验证码生成工具 package ...

    Security的玩法是跟着江南一点雨学习的。

    本文是跟着江南一点雨学习的学习笔记!

    1.自定义认证逻辑

    不破坏原有的过滤器链,又实现了自定义认证功能(基于Session,不是JSON交互)
    在这里插入图片描述

    • (1)验证码生成工具
    package com.oldbai.Util;
    
    import com.google.code.kaptcha.Producer;
    import com.google.code.kaptcha.impl.DefaultKaptcha;
    import com.google.code.kaptcha.util.Config;
    import org.springframework.context.annotation.Bean;
    
    import javax.imageio.ImageIO;
    import java.awt.*;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.util.Properties;
    import java.util.Random;
    
    /**
     * 生成验证码的工具类
     */
    public class VerifyCode {
        private int width = 100;// 生成验证码图片的宽度
        private int height = 50;// 生成验证码图片的高度
        private String[] fontNames = { "宋体", "楷体", "隶书", "微软雅黑" };
        private Color bgColor = new Color(255, 255, 255);// 定义验证码图片的背景颜色为白色
        private Random random = new Random();
        private String codes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
        private String text;// 记录随机字符串
    
        /**
         * 获取一个随意颜色
         *
         * @return
         */
        private Color randomColor() {
            int red = random.nextInt(150);
            int green = random.nextInt(150);
            int blue = random.nextInt(150);
            return new Color(red, green, blue);
        }
    
        /**
         * 获取一个随机字体
         *
         * @return
         */
        private Font randomFont() {
            String name = fontNames[random.nextInt(fontNames.length)];
            int style = random.nextInt(4);
            int size = random.nextInt(5) + 24;
            return new Font(name, style, size);
        }
    
        /**
         * 获取一个随机字符
         *
         * @return
         */
        private char randomChar() {
            return codes.charAt(random.nextInt(codes.length()));
        }
    
        /**
         * 创建一个空白的BufferedImage对象
         *
         * @return
         */
        private BufferedImage createImage() {
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2 = (Graphics2D) image.getGraphics();
            g2.setColor(bgColor);// 设置验证码图片的背景颜色
            g2.fillRect(0, 0, width, height);
            return image;
        }
    
        public BufferedImage getImage() {
            BufferedImage image = createImage();
            Graphics2D g2 = (Graphics2D) image.getGraphics();
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < 4; i++) {
                String s = randomChar() + "";
                sb.append(s);
                g2.setColor(randomColor());
                g2.setFont(randomFont());
                float x = i * width * 1.0f / 4;
                g2.drawString(s, x, height - 15);
            }
            this.text = sb.toString();
            drawLine(image);
            return image;
        }
    
        /**
         * 绘制干扰线
         *
         * @param image
         */
        private void drawLine(BufferedImage image) {
            Graphics2D g2 = (Graphics2D) image.getGraphics();
            int num = 5;
            for (int i = 0; i < num; i++) {
                int x1 = random.nextInt(width);
                int y1 = random.nextInt(height);
                int x2 = random.nextInt(width);
                int y2 = random.nextInt(height);
                g2.setColor(randomColor());
                g2.setStroke(new BasicStroke(1.5f));
                g2.drawLine(x1, y1, x2, y2);
            }
        }
    
        public String getText() {
            return text;
        }
    
        public static void output(BufferedImage image, OutputStream out) throws IOException {
            ImageIO.write(image, "JPEG", out);
        }
    
        /**
         * 提供一个实体类,使用网上一个现成的验证码库 kaptcha
         * @return
         */
        @Bean
        Producer verifyCode(){
            Properties properties = new Properties();
            properties.setProperty("kaptcha.image.width", "150");
            properties.setProperty("kaptcha.image.height", "50");
            properties.setProperty("kaptcha.textproducer.char.string", "0123456789");
            properties.setProperty("kaptcha.textproducer.char.length", "4");
            Config config = new Config(properties);
            DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
            defaultKaptcha.setConfig(config);
            return defaultKaptcha;
        }
    }
    
    
    • (2)验证码获取接口
    package com.oldbai.controller;
    
    import com.oldbai.Util.VerifyCode;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.imageio.ImageIO;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.awt.image.BufferedImage;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    @RestController
    public class LoginController {
    
        //提供生成图片的接口
        @GetMapping("/verifyCode")
        public void verifyCode(HttpSession session, HttpServletResponse response) throws IOException {
            VerifyCode code = new VerifyCode();
            BufferedImage image = code.getImage();
    //        检查是否生成图片
            ImageIO.write(image,"JPEG",new FileOutputStream("F:/a.jpg"));
            String text = code.getText();
            session.setAttribute("verify_code",text);
            VerifyCode.output(image,response.getOutputStream());
        }
    }
    
    
    • (3)在过滤器中进行验证码校验
    package com.oldbai.config;
    
    import org.springframework.security.authentication.AuthenticationServiceException;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    
    public class MyAuthenticationProvider extends DaoAuthenticationProvider {
        /**
         * 目的是在于,验证验证码,只需要在登陆请求中验证即可。
         * 之前的过滤器没问题,只是这个是更加高级的玩法
         * 这样既不破坏原有的过滤器链,又实现了自定义认证功能。
         * <p>
         * 首先获取当前请求,注意这种获取方式,在基于 Spring 的 web 项目中,我们可以随时随地获取到当前请求,获取方式就是我上面给出的代码。
         * 从当前请求中拿到 code 参数,也就是用户传来的验证码。
         * 从 session 中获取生成的验证码字符串。
         * 两者进行比较,如果验证码输入错误,则直接抛出异常。
         * 最后通过 super 调用父类方法,也就是 DaoAuthenticationProvider 的 additionalAuthenticationChecks 方法,该方法中主要做密码的校验。
         * </p>
         */
        @Override
        protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
            HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String code = req.getParameter("code");
            String verify_code = (String) req.getSession().getAttribute("verify_code");
            if (code == null || verify_code == null || !code.toLowerCase().equals(verify_code.toLowerCase())) {
                throw new AuthenticationServiceException("验证码错误");
            }
            super.additionalAuthenticationChecks(userDetails, authentication);
    
        }
    }
    
    
    • (4)在SecurityConfig中进行配置过滤器(因为我是使用MP-Security数据库认证,所以可以这样直接简单配制)
        /**
         * <p>
         *   所有的 AuthenticationProvider 都是放在 ProviderManager 中统一管理的,
         *   所以接下来我们就要自己提供 ProviderManager,
         *   然后注入自定义的 MyAuthenticationProvider
         * </p>
         * <P>
         *    我们需要提供一个 MyAuthenticationProvider 的实例,
         *    创建该实例时,需要提供 UserDetailService 和 PasswordEncoder 实例。
         * </P>
         * <p>
         *    通过重写 authenticationManager 方法来提供一个自己的 AuthenticationManager,
         *    实际上就是 ProviderManager,
         *    在创建 ProviderManager 时,加入自己的 myAuthenticationProvider。
         * </p>
         */
    
        @Bean
        MyAuthenticationProvider myAuthenticationProvider(){
            MyAuthenticationProvider myAuthenticationProvider = new MyAuthenticationProvider();
            myAuthenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder());
            myAuthenticationProvider.setUserDetailsService(userService);
            return myAuthenticationProvider;
        }
        @Override
        @Bean
        protected AuthenticationManager authenticationManager() throws Exception {
            ProviderManager manager = new ProviderManager(Arrays.asList(myAuthenticationProvider()));
            return manager;
        }
    
    • (5)实验截图
      成功案例1

    2.让 Spring Security 中的资源可以匿名访问

    • SecurityConfig 中添加配制
      /**
         * 解决Spring Security 登录成功后总是获取不到登录用户信息
         * <p>
         *     在不同线程中,不能获取同一个用户登陆信息
         * </p>
         * <p>
         *     这是不走过滤器的解决方法
         * 让 Spring Security 中的资源可以匿名访问
         * 不走 Spring Security 过滤器链
         * 登陆接口如果放在这里,登录请求将不走 SecurityContextPersistenceFilter 过滤器,
         * 也就意味着不会将登录用户信息存入 session,进而导致后续请求无法获取到登录用户信息。
         * 下面是放行静态资源:/css/**、/js/**、/index.html、/img/**、/fonts/**、/favicon.ico
         * 放行接口:/verifyCode
         * 不能把登陆接口放这
         * </p>
         */
        @Override
        public void configure(WebSecurity web) throws Exception {
            web.ignoring().antMatchers("/css/**","/js/**","/index.html","/img/**","/fonts/**","/favicon.ico","/verifyCode");
        }
    

    3. 保存关于 Http 请求的更多信息(变相的验证码验证)

    当然,WebAuthenticationDetails 也可以自己定制,因为默认它只提供了 IP 和 sessionid 两个信息,如果我们想保存关于 Http 请求的更多信息,就可以通过自定义 WebAuthenticationDetails 来实现。
    如果我们要定制 WebAuthenticationDetails,还要连同 WebAuthenticationDetailsSource 一起重新定义。
    里面主要保存 SessionId 和 用户IP地址,也可以自定义保存其他东西。

    • (1)MyWebAuthenticationDetails
    package com.oldbai.config;
    import org.springframework.security.web.authentication.WebAuthenticationDetails;
    import javax.servlet.http.HttpServletRequest;
    
    public class MyWebAuthenticationDetails extends WebAuthenticationDetails {
        /**
         * 用来保存是否验证正确
         */
        private boolean isPassed;
        private String v_code;
        /**
         * <p>
         *     如果我们想扩展属性,只需要在 MyWebAuthenticationDetails 中再去定义更多属性,
         *     然后从 HttpServletRequest 中提取出来设置给对应的属性即可,
         *     这样,在登录成功后就可以随时随地获取这些属性了。
         * </p>
         */
        public MyWebAuthenticationDetails(HttpServletRequest request) {
            super(request);
            String code = request.getParameter("code");
            this.v_code = code;
            String verify_code = (String) request.getSession().getAttribute("verify_code");
            if (code != null && verify_code != null && code.toLowerCase().equals(verify_code.toLowerCase())) {
                isPassed = true;
            }
        }
    
        public boolean isPassed(){
            return isPassed;
        }
    
        public String getV_code() {
            return v_code;
        }
    
        public void setV_code(String v_code) {
            this.v_code = v_code;
        }
    }
    
    
    • (2)MyWebAuthenticationDetailsSource
    @Component
    public class MyWebAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest,MyWebAuthenticationDetails> {
    
        @Override
        public MyWebAuthenticationDetails buildDetails(HttpServletRequest context) {
            return new MyWebAuthenticationDetails(context);
        }
    }
    
    
    • (3)MyAuthenticationProvider(用上面1的代码进行改写)
    public class MyAuthenticationProvider extends DaoAuthenticationProvider {
        @Override
        protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
            MyWebAuthenticationDetails details = (MyWebAuthenticationDetails) authentication.getDetails();
            if (!details.isPassed()) {
                throw new AuthenticationServiceException("验证码错误");
            }
            super.additionalAuthenticationChecks(userDetails, authentication);
    
        }
    }
    
    • (4)SecurityConfig(添加配制)
    	/**
         * 自定义的myWebAuthenticationDetailsSource替换系统默认的WebAuthenticationDetailsSource
         */
        @Autowired
        MyWebAuthenticationDetailsSource myWebAuthenticationDetailsSource;
        
        @Override
        protected void configure(HttpSecurity http) throws Exception {
    	    http.formLogin()
    	                .xxx
    	                .authenticationDetailsSource(myWebAuthenticationDetailsSource)
    	                .xxx
    	}
    
    • (5)测试接口
    /**
         * 测试接口
         * @return
         */
        @GetMapping("/hello")
        public MyWebAuthenticationDetails HelloWorld() {
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            MyWebAuthenticationDetails details = (MyWebAuthenticationDetails) auth.getDetails();
            return details;
        }
    
    • (6)测试结果

    测试结果

    4.踢掉上一个登陆用户

    (1)基于用户内存的方法

    • 直接在 SecurityConfig 中配制
    	@Bean
        HttpSessionEventPublisher httpSessionEventPublisher(){
            return new HttpSessionEventPublisher();
        }
        @Override
        protected void configure(HttpSecurity http) throws Exception {
    	/**
             * <p>
             *     想要用新的登录踢掉旧的登录,我们只需要将最大会话数设置为 1 即可
             * </p>
             * <p>
             *     设置session会话最大会话数为 1
             * </p>
             * <p>
             *      禁止新的登陆操作
             * </p>
             *
             */
            http.sessionManagement()
                    .maximumSessions(1)
                    .maxSessionsPreventsLogin(true);
    }
    

    (2)前后端分离,使用数据库认证

    • 直接在User类里面添加这两个,重写方法
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            User user = (User) o;
            return Objects.equals(username, user.username);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(username);
        }
    

    (3)前后端分离,JSON交互

    • ①SecurityConfig进行配置

    这里我们要自己提供 SessionAuthenticationStrategy,
    而前面处理 session 并发的是 ConcurrentSessionControlAuthenticationStrategy,
    也就是说,我们需要自己提供一个 ConcurrentSessionControlAuthenticationStrategy 的实例,
    然后配置给 LoginFilter,
    但是在创建 ConcurrentSessionControlAuthenticationStrategy 实例的过程中,
    还需要有一个 SessionRegistryImpl 对象

    @Bean
    SessionRegistryImpl sessionRegistry() {
        return new SessionRegistryImpl();
    }
    
    • ②在 SecurityConfig 中的 LoginFilter 中配置 SessionAuthenticationStrategy

    在这里自己手动构建 ConcurrentSessionControlAuthenticationStrategy 实例,构建时传递 SessionRegistryImpl 参数,然后设置 session 的并发数为 1,最后再将 sessionStrategy 配置给 LoginFilter。

    /**
    *手动构建 ConcurrentSessionControlAuthenticationStrategy 实例,构建时传递 *SessionRegistryImpl 参数,然后设置 session 的并发数为 1,最后再将 sessionStrategy 配置给 *LoginFilter
    */
    @Bean
    LoginFilter loginFilter() throws Exception {
        LoginFilter loginFilter = new LoginFilter();
        loginFilter.setAuthenticationSuccessHandler((request, response, authentication) -> {
                    //...
                }
        );
        loginFilter.setAuthenticationFailureHandler((request, response, exception) -> {
                    //...
                }
        );
        loginFilter.setAuthenticationManager(authenticationManagerBean());
        loginFilter.setFilterProcessesUrl("/doLogin");
        
        ConcurrentSessionControlAuthenticationStrategy sessionStrategy = new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry());
        sessionStrategy.setMaximumSessions(1);
        loginFilter.setSessionAuthenticationStrategy(sessionStrategy);
        
        return loginFilter;
    }
    
    • ③在SecurityConfig 中的 http 的config 中添加配制

    重新创建一个 ConcurrentSessionFilter 的实例,代替系统默认的即可。
    在创建新的 ConcurrentSessionFilter 实例时,需要两个参数:
    sessionRegistry 就是我们前面提供的 SessionRegistryImpl 实例。
    第二个参数,是一个处理 session 过期后的回调函数,也就是说,当用户被另外一个登录踢下线之后,你要给什么样的下线提示,就在这里来完成。

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                ...
        http.addFilterAt(new ConcurrentSessionFilter(sessionRegistry(), event -> {
            HttpServletResponse resp = event.getResponse();
            resp.setContentType("application/json;charset=utf-8");
            resp.setStatus(401);
            PrintWriter out = resp.getWriter();
            out.write(new ObjectMapper().writeValueAsString(RespBean.error("您已在另一台设备登录,本次登录已下线!")));
            out.flush();
            out.close();
        }), ConcurrentSessionFilter.class);
        http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
    }
    
    • ④手动向 SessionRegistryImpl 中添加一条记录

    手动调用 sessionRegistry.registerNewSession 方法,向 SessionRegistryImpl 中添加一条 session 记录。

    public class LoginFilter extends UsernamePasswordAuthenticationFilter {
        @Autowired
        SessionRegistry sessionRegistry;
        @Override
        public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
            //省略
                UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
                        username, password);
                setDetails(request, authRequest);
                User principal = new User();
                
                principal.setUsername(username);
                sessionRegistry.registerNewSession(request.getSession(true).getId(), principal);
                
                return this.getAuthenticationManager().authenticate(authRequest);
            } 
            ...
            ...
        }
    }
    
    • ⑤使用JSON交互的登陆,比基于数据库需要多几个步骤是:

    1.配制一个SessionRegistryImpl
    2.在 SecurityConfig 中的 LoginFilter 中配置 SessionAuthenticationStrategy
    3.在SecurityConfig 中的 http 的config 中添加配制
    4.手动向 SessionRegistryImpl 中添加一条记录
    推荐有项目进行测试,反正我在postman测试没成功

    5.跨域配制

    • SecurityConfig
    @Override
        protected void configure(HttpSecurity http) throws Exception {
    		/**
             * <p>
             *     开启跨域
             * </p>
             */
            http.cors().configurationSource(corsConfigurationSource());
    }
        /**
         * <p>
         * 开启跨域
         * </p>
         * <p>
         * 通过 CorsConfigurationSource 实例对跨域信息作出详细配置,
         * 例如允许的请求来源、
         * 允许的请求方法、
         * 允许通过的请求头、
         * 探测请求的有效期、
         * 需要处理的路径
         * 等等。
         * </p>
         */
        @Bean
        CorsConfigurationSource corsConfigurationSource() {
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            CorsConfiguration configuration = new CorsConfiguration();
            configuration.setAllowCredentials(true);
            configuration.setAllowedOrigins(Arrays.asList("*"));
            configuration.setAllowedMethods(Arrays.asList("*"));
            configuration.setAllowedHeaders(Arrays.asList("*"));
            configuration.setMaxAge(Duration.ofHours(1));
            source.registerCorsConfiguration("/**", configuration);
            return source;
        }
    

    6.csrf 攻击如何防御

    		/**
            * 前后端分离中
            * 不是将 _csrf 放在 Model 中返回前端了,
            * 而是放在 Cookie 中返回前端
             * <p>
             *     前端需要从cookie 中的'XSRF-TOKEN' 提取 _csrf 的值交给后端
             *     通过一个 POST 请求执行操作,注意携带上 _csrf 参数
             * </p>
            */
            http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
    

    松哥笔记

    在这里插入图片描述

    7.更新当前用户信息

        /**
         * 先获取当前用户信息
         */
        @GetMapping("/hr/info")
        public User getCurrentHr(Authentication authentication) {
            return ((User) authentication.getPrincipal());
        }
        /**
         * 更新最新当前用户信息
         *
         */
        @PostMapping("/hr/info")
        public String updata(@RequestBody User user, Authentication authentication) {
    
            if (userService.saveOrUpdate(user)){
                SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(user,authentication.getCredentials(),authentication.getAuthorities()));
                return "更新成功";
            }
    
            return "更新失败";
        }
    

    8.防止会话固定攻击

    默认的 migrateSession ,在用户匿名访问的时候是一个 sessionid,当用户成功登录之后,又是另外一个 sessionid,这样就可以有效避免会话固定攻击。

    • migrateSession 表示在登录成功之后,创建一个新的会话,然后把旧的 session 中的信息复制到新的 session 中,「默认即此」

    http.sessionManagement().sessionFixation().migrateSession();

    • none 表示不做任何事情,继续使用旧的 session。

    http.sessionManagement().sessionFixation().none ();

    • changeSessionId 表示 session 不变,但是会修改 sessionid,这实际上用到了 Servlet 容器提供的防御会话固定攻击。

    http.sessionManagement().sessionFixation().changeSessionId ();

    • newSession 表示登录后创建一个新的 session。

    http.sessionManagement().sessionFixation().newSession ();

    写在最后

    • 学习链接 江南一点雨
    • 以上这些,对于个人的练习开放小项目,足够了。
    • 个人思路构建一个小型项目逻辑

    1.创建工程,导入MyBatis-Plus 依赖,进行配置,单元测试是否连接成功
    2.导入Security 依赖,进行配置
    3.使用MP与Security进行整合,基于数据库的认证。
    4.进行角色等级配置
    5.设置最大会话数,也就是踢掉登陆或者不让登陆
    6.配置登陆成功、失败、无状态访问、注销回调
    7.配置验证码生成工具,开放验证码接口
    8.自定义一个登陆逻辑过滤器用于验证验证码是否正确
    9.如果是前后端分离项目,进行跨域配制
    10.防止固定会话、开启csrf 防御,前端记得要从cookid中拿到并携带 _csrf 的参数进行请求
    11.更新用户信息的配制,主要是在接口中进行配置
    12.修改密码的配制,还没会,所以先不急。

    展开全文
  • 趁着这个周日时间,来mark一下江南一点雨的微人事项目。 微人事是一个前后端分离的人力资源管理系统,项目采用SpringBoot+Vue开发。 git地址:https://github.com/lenve/vhr 步骤: 1)从git上拉取项目 2) ...

    前言:

    mark一下江南一点雨的微人事项目。

    微人事是一个前后端分离的人力资源管理系统,项目采用SpringBoot+Vue开发。

    微人事部署视频:https://mp.weixin.qq.com/s/dcJv6BIVFPnokI8nBf4IrQ

    git地址:https://github.com/lenve/vhr

    码云地址:https://gitee.com/lenve/vhr

    工具:

    后端软件开发的工具:Idea

    前端软件开发的工具 : vscode

    步骤:

    第一部分启动后台

     1)从git上拉取项目,也可以直接从码云上拉取项目。

     

    2) 数据库新建一个vhr的数据库。

     

     

    3)使用idea打开这个项目

     

     

    打开后的项目就是这样子:

     

    4) 准备redis  ,由于我电脑已经有了redis,将配置文件中的替换一下。

    5)准备rabbitmq ,没有安装的可以参考 window10下安装rabbitmq   

     

    6)项目导入idea 需要配置项目中的信息,更改为自己的参数

    可以看到这个项目我的端口是8087

    rabbitmq 安装在192.169.20.193这台服务器上。

     

    修改之后:

    spring:
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        username: root
        password: root
        url: jdbc:mysql://localhost:3306/vhr?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
      rabbitmq:
        username: guest
        password: guest
        host: 192.168.20.193
        publisher-confirms: true
        publisher-returns: true
      redis:
        host: 127.0.0.1
        database: 0
        port: 6379
    #    password: 123
      cache:
        cache-names: menus_cache
    server:
      port: 8087
      compression:
        enabled: true
    fastdfs:
      nginx:
        host: http://192.168.20.193/

     

     

    在浏览器上访问:localhost:8087/index.html

     

    点击登录

    项目就跑起来了 ,这个时候再看一下我们的数据库,发现数据库也已经自动生成了。

     第二部分启动前端

    (1)前端项目需要准备node js

     

    我这边使用的是vscode软件打开这个前端项目

     

    项目打开以后我们可以安装依赖,在vscode中打开终端 输入npm install

     

    2)然后启动 npm run serve启动项目

    这里需要注意的点:

    前端项目启动成功以后 我们需要配置连接信息,配置好以后我们就可以在浏览器上访问啦。

    二次开发完成后执行 npm run build 编译打包,将编译好的文件放到后台的static文件夹下。

    在浏览器上访问:localhost:8080/#/

    登录系统以后可以看到后台项目里的数据

    我们可以试着编辑一条数据

    把我们的博主大大江南一点雨设置为技术总监,点击确定。

    刷新网页以后也是可以的,说明连接上数据库 ,不是一些假数据。

     

    这篇博客我们将微人事这个项目简单的搭建起来了,这是一个前后端分离的项目,后端采用的技术有 redis rabbitMq SpringBoot.

    前端采用的是vue,我觉得这个开源项目真的很值得我们去学习,学习别人写的优秀的地方。

    再一次为博主江南一点雨打call, 等我再研究明白一点以后再出微人事的其他篇

    感谢你的阅读,如果觉得小编写的不错的话不妨给小编一个赞吧。谢谢。

     

     

    展开全文
  • 转载自_江南一点雨 博主 原谅我愚昧,Intent七大属性这个概念我也是昨天才接触到,看了一下,都是一些常用的东西,就是没有总结过,那么今天就来简单总结一下。 Intent七大属性是指Intent的ComponentName、...

    转载自_江南一点雨  博主

    原谅我愚昧,Intent七大属性这个概念我也是昨天才接触到,看了一下,都是一些常用的东西,就是没有总结过,那么今天就来简单总结一下。

    Intent七大属性是指Intent的ComponentName、Action、Category、Data、Type、Extra以及Flag,七个属性,总体上可以分为3类:

    第一类:启动,有ComponentName(显式),Action(隐式),Category(隐式)。

    第二类:传值,有Data(隐式),Type(隐式),Extra(隐式、显式)。

    第三类:启动模式,有Flag。

    下面我们逐一来说。

    1.ComponentName

    Component本身有组件的意思,我们通过设置Component可以启动其他的Activity或者其他应用中的Activity,来看一个简单的实例:

    启动同一个App中另外一个Activity:

    1. intent = new Intent();  
    2.             intent.setComponent(new ComponentName(this, SecondActivity.class));  
    3.             startActivity(intent);  
    这中启动方式等同于以下两种启动方式:

    1. intent = new Intent(this,SecondActivity.class);  
    2.             startActivity(intent);  

    1. intent = new Intent();  
    2.             intent.setClass(this, SecondActivity.class);  
    3.             startActivity(intent);  
    当然,通过设置ComponentName属性我们也可以启动其他App中的Activity,关于这一块的内容大家可以参考关于ComponentName的使用。下面我们看看隐式启动。

    2.Action和Category

    因为在实际开发中,Action大多时候都是和Category一起使用的,所以这里我们将这两个放在一起来讲解。Intent中的Action我们在使用广播的时候用的比较多,在Activity中,我们可以通过设置Action来隐式的启动一个Activity,比如我们有一个ThirdActivity,我们在清单文件中做如下配置:

    1. <activity  
    2.     android:name=".ThirdActivity"  
    3.     android:label="@string/title_activity_third" >  
    4.     <intent-filter>  
    5.         <category android:name="android.intent.category.DEFAULT" />  
    6.   
    7.   
    8.         <action android:name="com.qf.ThirdActivity" />  
    9.     </intent-filter>  
    10. </activity>  
    当我们在清单文件中做了这样的配置之后,我们的ThirdActivity会就会响应这个动作,怎么那么怎么响应呢?看下面:

    1. intent = new Intent();  
    2. intent.setAction("com.qf.ThirdActivity");  
    3. startActivity(intent);  
    当然,我们也可以写的更简单一些,如下:

    1. intent = new Intent("com.qf.ThirdActivity");  
    2.             startActivity(intent);  
    通过这中方式我们也可以启动一个Activity,那么大家可能也注意到了,我们的清单文件中有一个category的节点,那么没有这个节点可以吗?不可以!!当我们使用这种隐式启动的方式来启动一个Activity的时候,必须要action和category都匹配上了,该Activity才会成功启动。如果我们没有定义category,那么可以暂时先使用系统默认的category,总之,category不能没有。这个时候我们可能会有疑问了,如果我有多个Activity都配置了相同的action,那么会启动哪个?看看下面这个熟悉的图片:

    当我们有多个Activity配置了相同的action的时候,那么系统会弹出来一个选择框,让我们自己选择要启动那个Activity。

    action我们只能添加一个,但是category却可以添加多个(至少有一个,没有就要设置为DEFAULT),如下:

    1. <activity  
    2.     android:name=".ThirdActivity"  
    3.     android:label="@string/title_activity_third" >  
    4.     <intent-filter>  
    5.         <category android:name="android.intent.category.DEFAULT" />  
    6.         <category android:name="mycategory" />  
    7.   
    8.         <action android:name="com.qf.ThirdActivity" />  
    9.     </intent-filter>  
    10. </activity>  
    相应的我们的启动方式也可以修改,如下:

    1. intent = new Intent("com.qf.ThirdActivity");  
    2.             intent.addCategory("mycategory");  
    3.             startActivity(intent);  

    3.Data

    通过设置data,我们可以执行打电话,发短信,开发网页等等操作。究竟做哪种操作,要看我们的数据格式:

    1. // 打开网页  
    2. intent = new Intent(Intent.ACTION_VIEW);  
    3. intent.setData(Uri.parse("http://www.baidu.com"));  
    4. startActivity(intent);  
    5. // 打电话  
    6. intent = new Intent(Intent.ACTION_VIEW);  
    7. intent.setData(Uri.parse("tel:18565554482"));  
    8. startActivity(intent);  
    当我们的data是一个http协议的时候,系统会自动去查找可以打开http协议的Activity,这个时候如果手机安装了多个浏览器,那么系统会弹出多个浏览器供我们选择。这是我们通过设置Data来启动一个Activity,同时,我们也可以通过设置一个Data属性来将我们的Activity发布出去供别人调用,怎么发布呢?

    1. <activity  
    2.     android:name=".HttpActivity"  
    3.     android:label="@string/title_activity_http" >  
    4.     <intent-filter>  
    5.         <action android:name="android.intent.action.VIEW" />  
    6.   
    7.         <category android:name="android.intent.category.DEFAULT" />  
    8.   
    9.         <data  
    10.             android:scheme="http" />  
    11.     </intent-filter>  
    12. </activity>  

    在data节点中我们设置我们这个Activity可以打开的协议,我们这里设置为http协议,那么以后要打开一个http请求的时候,系统都会让我们选择是否用这个Activity打开。当然,我们也可以自己定义一个协议(自己定义的协议,由于别人不知道,所以只能由我们自己的程序打开)。比如下面这样:

    1. <activity  
    2.     android:name=".HttpActivity"  
    3.     android:label="@string/title_activity_http" >  
    4.     <intent-filter>  
    5.         <action android:name="android.intent.action.VIEW" />  
    6.   
    7.         <category android:name="android.intent.category.DEFAULT" />  
    8.   
    9.         <data  
    10.             android:scheme="myhttp" />  
    11.     </intent-filter>  
    12. </activity>  
    那么我们怎么打开自己的Activity呢?

    1. intent = new Intent();  
    2.             intent.setData(Uri.parse("myhttp://www.baidu.com"));  
    3.             startActivity(intent);  
    这个例子没有什么实际意义,我只是举一个自定义协议的栗子。

    其实,说到这里,大家应该明白了为什么我们说data是隐式传值,比如我们打开一个网页,http协议后面跟的就是网页地址,我们不用再单独指定要打开哪个网页。

    4.Type

    type的存在,主要是为了对data的类型做进一步的说明,但是一般情况下,只有data属性为null的时候,type属性才有效,如果data属性不为null,系统会自动根据data中的协议来分析data的数据类型,而不会去管type,我们先来看看下面一段源码:

    1. /** 
    2.  * Set the data this intent is operating on.  This method automatically 
    3.  * clears any type that was previously set by {@link #setType} or 
    4.  * {@link #setTypeAndNormalize}. 
    5.  * 
    6.  * <p><em>Note: scheme matching in the Android framework is 
    7.  * case-sensitive, unlike the formal RFC. As a result, 
    8.  * you should always write your Uri with a lower case scheme, 
    9.  * or use {@link Uri#normalizeScheme} or 
    10.  * {@link #setDataAndNormalize} 
    11.  * to ensure that the scheme is converted to lower case.</em> 
    12.  * 
    13.  * @param data The Uri of the data this intent is now targeting. 
    14.  * 
    15.  * @return Returns the same Intent object, for chaining multiple calls 
    16.  * into a single statement. 
    17.  * 
    18.  * @see #getData 
    19.  * @see #setDataAndNormalize 
    20.  * @see android.net.Uri#normalizeScheme() 
    21.  */  
    22. public Intent setData(Uri data) {  
    23.     mData = data;  
    24.     mType = null;  
    25.     return this;  
    26. }  
    27.   
    28. /** 
    29.  * Set an explicit MIME data type. 
    30.  * 
    31.  * <p>This is used to create intents that only specify a type and not data, 
    32.  * for example to indicate the type of data to return. 
    33.  * 
    34.  * <p>This method automatically clears any data that was 
    35.  * previously set (for example by {@link #setData}). 
    36.  * 
    37.  * <p><em>Note: MIME type matching in the Android framework is 
    38.  * case-sensitive, unlike formal RFC MIME types.  As a result, 
    39.  * you should always write your MIME types with lower case letters, 
    40.  * or use {@link #normalizeMimeType} or {@link #setTypeAndNormalize} 
    41.  * to ensure that it is converted to lower case.</em> 
    42.  * 
    43.  * @param type The MIME type of the data being handled by this intent. 
    44.  * 
    45.  * @return Returns the same Intent object, for chaining multiple calls 
    46.  * into a single statement. 
    47.  * 
    48.  * @see #getType 
    49.  * @see #setTypeAndNormalize 
    50.  * @see #setDataAndType 
    51.  * @see #normalizeMimeType 
    52.  */  
    53. public Intent setType(String type) {  
    54.     mData = null;  
    55.     mType = type;  
    56.     return this;  
    57. }  

    当我们设置data的时候,系统会默认将type设置为null,当我们设置type的时候,系统会默认将data设置为null.也就是说,一般情况下,data和type我们只需要设置一个就行了,如果我们既想要设置data又想要设置type,那么可以使用
    1. setDataAndType(Uri data, String type)  

    这个方法来完成。下面我们来看看通过给Intent设置type来打开一个音乐播放器。代码如下:

    1. intent = new Intent();  
    2.             intent.setAction(Intent.ACTION_VIEW);  
    3.             Uri data = Uri.parse("file:///storage/emulated/0/xiami/audios/被动.mp3");  
    4.             intent.setDataAndType(data, "audio/mp3");  
    5.             startActivity(intent);  
    如果我们要打开的是视频文件,那么type就要设置为"video/*",其中*表示支持所有的视频文件。

    5.Extra

    Extra就比较好理解了,我们经常使用它来在Activity之间传递数据,Extra可以传递基本类型,String类型以及实现了Serializable或者Parcelable接口的类,具体用法不多说。

    6.Flag

    通过设置Flag,我们可以设定一个Activity的启动模式,这个和launchMode基本上是一样的,所以我也不再细说,关于launchMode的使用参见launchMode使用详解

    展开全文
  • 打包过程会稍微有一点旧,因为还包含了镜像的构建,特别是第一次打包,需要下载基础镜像,会更慢一些。 部分打包日志如下(项目构建过程): 项目打包成功之后,我们就可以在 Docker 容器中看到我们刚刚打包成...
  • 抄写 SpringCloud 笔记目的 最近在学习江南一点雨的 SpringClooud 视频内容,刚接触一个新知识,很多内容都比较陌生、晦涩,而学习的目的主要是能够独立的运用,代码或者视频影像是记忆不过来的,写笔记能回顾所看的...
  • 为了方便和大家交流,我在网上加了一些小伙伴,其中就有江南一点雨,在我印象中他在 CSDN 写了很多 Spring Boot/Cloud 系列文章。 但加完微信并没有多聊几句,就这样默默的消失在了我的微信列表中,直到有一天我们...
  • 一、Consul ...使用的方式也是大同小异。不同的是,consul由于是GO语言编写,所以不是完全整合在spring cloud中,需要额外运行管理端。 正因为如此,consul有很多个版本,教程里的版本相对较低,为了与教程保持一致,我...
  • Hystrix 1、简单了解 当我看完视频,去自己写demo的时候,spring boot版本为2.4.3。其实在spring boot的2.4.0之后的版本就不再支持Hystrix。但还是简单的看一下吧。 hystrix叫做断路器。因为微服务系统中由很多个...
  • OpenFeign 在之前的几章组件间相互访问都是手动调用RestTemplate,虽然这... 还有一点,在使用post提交请求时,如果想使用key/value形式传参,必须使用MultiValueMap类。 同时在接口中也可以使用之前的服务降级功能。
  • 一.... 3.最新版本的spring源码是基于 Gradle ([ɡreɪdl] ) 来构建的,所以我们要安装 Gradle 如果经常使用的可以全装一下... 原视频网站:https://www.bilibili.com/video/BV1ui4y137K3 也可以关注公众号: 江南一点雨

空空如也

空空如也

1 2 3 4 5 ... 12
收藏数 234
精华内容 93
关键字:

江南一点雨