欢迎光临
我们一直在努力

1-使用SpringSecurity框架登录认证查询数据库用户(前后端不分离,前端使用thymeleaf)

  • 先创建一个处理登录的service接口,需要继承spring security框架的UserDetailsService接口

  • 创建接口实现类UserServiceImpl,在loadUserByUsername方法中写用户验证逻辑

  • 同时编写xml中的登录逻辑代码

  • 通过loadUserByUsername方法发现,返回值必须是UserDetails接口,所以需要构建一个Spring Security框架里面的User对象(这个User对象是UserDetails接口的实现类)来返回。

  • 编写配置文件SecurityConfig

    • 创建一个配置文件包config,在这里面写配置文件类SecurityConfig,

      • 5-1首先需要写一个密码加密器

      • 5-2在配置文件(SecurityConfig)配置我们自己的登录页,不使用框架默认的登录页

      • 5-3这个案例使用前后端不分离,前端使用thymeleaf,首先需要添加thymeleaf依赖

      • 5-4添加静态登录页面login.html,放在src/main/resources/templates

      • 5-5编写controller,设置登录地址的跳转

  • 添加图形验证码功能

    • 6-1添加hutool-captcha依赖

    • 6-2编写一个验证码接口Controller类(CaptchaController)

    • 6-3在SecurityConfig文件中放行获取验证码的访问地址

    • 6-4创建一个对验证码拦截的filtter类,对验证码经行验证,

    • 同时需要将自己编写的filtter加入到SpringSecurity的拦截链中:

      • 在CaptchaFilter上加@Component注解

      • 6-5在SecurityConfig文件中添加一条链

  • 1-service接口


    package com.bjpowernode.service;

    import org.springframework.security.core.userdetails.UserDetailsService;

    //我们的处理登录的service接口,需要继承spring security框架的UserDetailsService接口
    public interface UserService extends UserDetailsService {

    }

    2-接口实现类UserServiceImpl

    package com.bjpowernode.service.impl;

    import com.bjpowernode.entity.TUser;
    import com.bjpowernode.mapper.TUserMapper;
    import com.bjpowernode.service.UserService;
    import jakarta.annotation.Resource;
    import org.springframework.security.core.authority.AuthorityUtils;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.stereotype.Service;

    @Service
    public class UserServiceImpl implements UserService {

       //逆向工程、反向工程(根据数据库表,生成mapper接口、mapper.xml、实体类)

       @Resource
       private TUserMapper tUserMapper;

       /**
        * 该方法在spring security框架登录的时候被调用
        *
        * @param username
        * @return
        * @throws UsernameNotFoundException
        */
       @Override
       public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
           //查询数据库,查询页面上传过来的这个用户名是否在数据库中存在,也就是根据该username查询用户对象
           TUser tUser = tUserMapper.selectByLoginAct(username); //cat
           if (tUser == null) {
               throw new UsernameNotFoundException("登录账号不存在");
          }

           //构建一个Spring Security框架里面的User对象来返回
           UserDetails userDetails = User.builder()
                  .username(tUser.getLoginAct())
                  .password(tUser.getLoginPwd())
                  .authorities(AuthorityUtils.NO_AUTHORITIES) //权限是空的
                   //.accountExpired(true) //true表示账号过期了
                  .build();
           return userDetails; //把UserDetails(User)返回给框架之后 ,框架会采用密码加密器进行密码的比较
      }
    }

    3-xml中的登录逻辑代码

    <select id="selectByLoginAct" parameterType="java.lang.String" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from t_user
    where login_act = #{loginAct, jdbcType=VARCHAR}
    </select>

    5-1密码加密器

    package com.bjpowernode.config;

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;

    @Configuration //配置spring的容器,类似spring.xml文件一样
    public class SecurityConfig {

       /**
        * <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"> </bean>
        *
        * @return
        */
       @Bean  //配置一个spring的bean, bean的id就是方法名,bean的class就是方法的返回类型
       public PasswordEncoder passwordEncoder() {
           return new BCryptPasswordEncoder();
      }
    }

    5-2配置我们自己的登录页,不使用框架默认的登录页

    //配置spring security框架的一些行为(配置我们自己的登录页,不使用框架默认的登录页)
    //但是当你配置了SecurityFilterChain这个Bean之后,Spring security框架的某些默认行为就弄丢了(失效了),此时你需要加回来(捡回来)
    @Bean //安全过滤器链Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
       //httpSecurity是方法参数注入Bean
       //在spring security框架开发时,创建SecurityFilterChain这个Bean,不是直接new DefaultSecurityFilterChain
       //return new DefaultSecurityFilterChain();

       return httpSecurity
               //配置我们自己的登录页
              .formLogin(new Customizer<FormLoginConfigurer<HttpSecurity>>() {
                   @Override
                   public void customize(FormLoginConfigurer<HttpSecurity> formLogin) {
           // 框架默认接收登录提交请求的地址是 /login,但是我们把它给弄丢了,需要捡回来
            //登录的账号密码往哪个地址提交        
                     formLogin.loginProcessingUrl("/user/login")
                            .loginPage("/toLogin"); //定制登录页(Thymeleaf页面)
                  }
              })

               //把所有接口都会进行登录状态检查的默认行为,再加回来
              .authorizeHttpRequests( (authorizeHttpRequests) -> {
                   authorizeHttpRequests
                          .requestMatchers("/toLogin").permitAll() //特殊情况的设置,permitAll允许不登录就可以访问
                          .anyRequest().authenticated(); //除了上面的特殊情况外,其他任何对后端接口的请求,都需要认证(登录)后才能访问
              })

              .build();
    }

    5-3添加thymeleaf依赖

    <!–thymeleaf依赖–>
    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

    5-4添加静态登录页面login.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
       <meta charset="UTF-8">
       <title>登录</title>
    </head>

    <body>
       <form action="/user/login" method="post">
          账号:<input type="text" name="username"> <br/>
          密码:<input type="password" name="password"> <br/>
          验证码:<input type="text" name="captcha"> <img src="/common/captcha"/> <br/>
           <input name="_csrf" type="hidden" th:value="${_csrf.token}" />
           <input type="submit" value="登 录">
       </form>
    </body>
    </html>

    5-5编写controller,设置登录地址的跳转

    package com.bjpowernode.controller;

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;

    @Controller //跳转到页面上
    public class UserController {
        //当访问/toLogin地址时,直接跳转到login.html,这个就是src/main/resources/templates下的静态登录页面
       @RequestMapping(value = "/toLogin")
       public String toLogin() {
           return "login";
      }
    }

    6-1添加hutool-captcha依赖

    <!– hutool-captcha –>
    <dependency>
       <groupId>cn.hutool</groupId>
       <artifactId>hutool-captcha</artifactId>
       <version>5.8.32</version>
    </dependency>

    6-2编写一个验证码接口Controller类(CaptchaController)

    package com.bjpowernode.controller;

    import cn.hutool.captcha.CaptchaUtil;
    import cn.hutool.captcha.ICaptcha;
    import com.bjpowernode.captcha.MyCodeGenerator;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;

    import java.io.IOException;

    @Controller //跳转页面
    public class CaptchaController {

       @RequestMapping(value = "/common/captcha")
       public void generateCaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException {

           //告诉浏览器,我的响应内容类型是图片,jpeg格式的图片
           response.setContentType("image/jpeg");

           //生成的是一个验证码的图片,我们不需要跳转页面,就是把生成的这个图片写出到浏览器就可以,以IO流的方式写出去

           //1、生成这个验证码图片
           //ICaptcha captcha = CaptchaUtil.createCircleCaptcha(90, 30, 4, 0, 1);
           //ICaptcha captcha = CaptchaUtil.createCircleCaptcha(90, 30, new MyCodeGenerator(), 15);

           ICaptcha captcha = CaptchaUtil.createGifCaptcha(90, 30, 4,10,1);
           //ICaptcha captcha = CaptchaUtil.createGifCaptcha(90, 30, 4, 10, 0.8f);

           //ICaptcha captcha = CaptchaUtil.createLineCaptcha(90, 30, 4, 10, 1);

           //ICaptcha captcha = CaptchaUtil.createShearCaptcha(90, 30, 4, 2, 1);

           //2、把图片里面的验证码字符串(有几个数字)在后端保存起来,因为后续前端提交过来,在后端需要验证提交的验证码对不对
           request.getSession().setAttribute("captcha", captcha.getCode());

           //3、把生成的验证码图片以io流的方式写出去(写出到浏览器)
           captcha.write(response.getOutputStream());
      }
    }

    6-3在SecurityConfig文件中放行获取验证码的访问地址

    //把所有接口都会进行登录状态检查的默认行为,再加回来
    .authorizeHttpRequests( (authorizeHttpRequests) -> {
       authorizeHttpRequests
              .requestMatchers("/toLogin", "/common/captcha").permitAll() //特殊情况的设置,permitAll允许不登录就可以访问
              .anyRequest().authenticated(); //除了上面的特殊情况外,其他任何对后端接口的请求,都需要认证(登录)后才能访问
    })

    6-4创建一个对验证码拦截的filtter类,对验证码经行验证

    package com.bjpowernode.filter;

    import jakarta.servlet.FilterChain;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import org.springframework.stereotype.Component;
    import org.springframework.util.StringUtils;
    import org.springframework.web.filter.OncePerRequestFilter;

    import java.io.IOException;

    /**
    * 接收前端的验证码,并对验证码进行验证
    *
    */
    @Component
    public class CaptchaFilter extends OncePerRequestFilter { //在spring框架中,实现Filter,直接继承OncePerRequestFilter更方便

       @Override
       protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
           String code = request.getParameter("captcha");
           String sessionCode = (String) request.getSession().getAttribute("captcha");

           String requestUri = request.getRequestURI(); //   /user/login

           if (requestUri.equals("/user/login")) { //如果是登录请求,我们就验证验证码,否则不需要验证验证码
               if (!StringUtils.hasText(code)) { //前面加了一个“!” 表示非,取反,那就是如果code是空的
                   //验证没通过
                   response.sendRedirect("/");
              } else if (!code.equalsIgnoreCase(sessionCode)) { //如果前端传过来的验证码和后端session中存放的验证码不相等
                   //验证没通过
                   response.sendRedirect("/");
              } else {
                   //验证码相等,可以放行,继续执行下一个filter
                   filterChain.doFilter(request, response);
              }
          } else {
               //不是登录请求,不需要验证验证码,直接放行
               filterChain.doFilter(request, response);
          }
      }
    }

    6-5在SecurityConfig文件中添加一条链

    //把所有接口都会进行登录状态检查的默认行为,再加回来
    .authorizeHttpRequests( (authorizeHttpRequests) -> {
       authorizeHttpRequests
              .requestMatchers("/toLogin", "/common/captcha").permitAll() //特殊情况的设置,permitAll允许不登录就可以访问
              .anyRequest().authenticated(); //除了上面的特殊情况外,其他任何对后端接口的请求,都需要认证(登录)后才能访问
    })

    //验证码filter加在接收登录账号密码的UsernamePasswordAuthenticationFilter之前
    .addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class)

    .build();

    赞(0)
    未经允许不得转载:171主机测评 » 1-使用SpringSecurity框架登录认证查询数据库用户(前后端不分离,前端使用thymeleaf)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址