springboot+springSecurity验证码实现异步登录

springboot+springSecurity验证码实现登录

添加依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.1</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>springbootdome2</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>springbootdome2</name>
	<description>Demo project for Spring Boot</description>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<!--mysql数据库驱动-->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<!--mybatis-->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.1.0</version>
		</dependency>
		<!-- mybatis逆向工程jar包 -->
		<dependency>
			<groupId>org.mybatis.generator</groupId>
			<artifactId>mybatis-generator-core</artifactId>
			<version>1.3.4</version>
		</dependency>

		<!--security认证授权-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<!--thymeleaf引擎模板-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>

		<!--对Thymeleaf添加Spring Security标签支持-->
		<dependency>
			<groupId>org.thymeleaf.extras</groupId>
			<artifactId>thymeleaf-extras-springsecurity5</artifactId>
		</dependency>
		<!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
        <!--jquery-->
        <dependency>
			<groupId>org.webjars</groupId>
			<artifactId>jquery</artifactId>
			<version>3.5.1</version>
		</dependency>

	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
			<plugin>
				<groupId>org.mybatis.generator</groupId>
				<artifactId>mybatis-generator-maven-plugin</artifactId>
				<version>1.3.2</version>
				<configuration>
					<overwrite>true</overwrite>
					<!--逆向工程generatorConfig.xml文件位置-->
					<configurationFile>src/main/resources/generator/generatorConfig.xml</configurationFile>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

添加项目配置

server.port=8081
#数据库连接配置
spring.datasource.url=jdbc:mysql://47.110.157.82:3306/sweetinn?characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=sweetinn
spring.datasource.password=123456

#mybatis整合
mybatis.type-aliases-package=com.example.demo.pojo
mybatis.mapper-locations= classpath:mapper/*.xml
logging.level.com.example.demo.dao=debug

#关闭thymeleaf缓存
spring.thymeleaf.cache=false

生成验证码接口

@Controller
@RequestMapping("/captcha")
public class CaptchaController {
    
    

    private int width=120; //图片宽度
    private int height=30; //图片高度
    private int  drawY=18; //图片内容在图片的起始位置
    private int  charcount=6;//验证码位数
    private String chars[]={
    
    "0","1","2","3","4","5","6","7","8","9",
            "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
            "A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"};

   private int space=15;//文字间隔
    @RequestMapping("/code")
    public void makeCaptchaCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
    
    
        BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);//创建BufferedImage对象,其作用相当于一图片
        Graphics g=image.getGraphics();        //创建Graphics对象,其作用相当于画笔
        g.setColor(Color.white); //设置画笔颜色
        g.fillRect(0,0,width,height); //矩形大小
        Font mfont=new Font("楷体",Font.BOLD,16);    //定义字体样式
        g.setColor(Color.black);

        StringBuffer stringBuffer = new StringBuffer();//记录验证码文字

        //文字
        int ran=0;
        int len=chars.length;
        for (int i=0;i<charcount;i++){
    
    
            ran= new Random().nextInt(len);
            g.setColor(getRandColor());
            g.drawString(chars[ran],(i+1)*space,drawY);
            stringBuffer.append(chars[ran]);
        }
        //干扰线
        for(int i=1;i<4;i++){
    
    
            g.setColor(getRandColor());
            int[] ints = makeLineDot();
            g.drawLine(ints[0],ints[1],ints[2],ints[3]);

        }

        //设置不缓存图片
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "No-cache");
        response.setDateHeader("Expires", 0);

        //将验证码存入session中
        request.getSession().setAttribute("code",stringBuffer.toString());

        response.setContentType("image/png");
        ServletOutputStream out = response.getOutputStream();
        ImageIO.write(image,"png",out);
        out.flush();
        out.close();
    }

    /*该方法主要作用是获得随机生成的颜色*/
      public Color getRandColor(){
    
    
                Random random=new Random ();
          int r=random.nextInt(255);    //随机生成RGB颜色中的r值
          int g=random.nextInt(255);    //随机生成RGB颜色中的g值
          int b=random.nextInt(255);    //随机生成RGB颜色中的b值
                 return new Color(r,g,b);
             }

             //干扰线数组
             public int[] makeLineDot(){
    
    
                 Random random=new Random ();
                int x1= random.nextInt(width/2);
                int y1= random.nextInt(height);
                int x2= random.nextInt(width);
                int y2= random.nextInt(height);
                 return new int[]{
    
    x1,y1,x2,x2};
             }
}

页面跳转接口

@Controller
public class IndexController {
    
    
    @RequestMapping({
    
    "/","/index"})
    public String home(String name,String passwd,boolean jzw,Model model){
    
    
        return "home/home"; //首页
    }
    @RequestMapping("/toLogin")
    public String login(Model model){
    
    
        return "mylogin";  //登录页
    }
    @RequestMapping("/level1/page/{id}")
    public String level1(@PathVariable("id") int id){
    
    
        return "level1/page"+id;
    }
    @RequestMapping("/level2/page/{id}")
    public String level2(@PathVariable("id") int id){
    
    
        return "level2/page"+id;
    }
    @RequestMapping("/level3/page/{id}")
    public String level3(@PathVariable("id") int id){
    
    
        return "level3/page"+id;
    }
}

添加自定义登录页面mylogin.html

<!DOCTYPE html>
<html lang="en" >
<script src="webjars/jquery/3.5.1/jquery.min.js"></script>
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>

<div style="text-align: center">

        登录名:<input id="name" /> <br/>
        密码:<input type="password" id="passwd"/><br/>
         验证码:<input  id="yzm"/><br/>
    <img id="imagecode" src="/captcha/code">
    <a href="javascript:void(0)" onclick="changeCode()"  >看不清?换一个</a><br/>
    <input type="checkbox" id="jzw"> 记住我<br/>
        <input type="button" value="登录" id="login"/>
</div>
</body>
<script type="application/javascript">
    $("#login").click(function () {
    
    
        var name=$("#name").val();
        var passwd=$("#passwd").val();
        var yzm=$("#yzm").val();
        var jzw=$("#jzw").val();
        $.ajax({
    
    
            url:"/login",
            type:"POST",
            dataType:"json",
            data:{
    
    
                name:name,
                passwd:passwd,
                yzm:yzm,
                jzw:jzw
            },
            success: function (resq) {
    
    
                if(resq.success){
    
    
                    window.location.href="/index";
                }else{
    
    
                    alert(resq.message);
                }
            }
        })
    })

    //重新获取验证码
    function changeCode() {
    
    
        //new Date()的目的是浏览器不使用缓存,每次获取新的验证码
         var url="/captcha/code?t="+new Date();
         $("#imagecode").attr("src",url);
    }
</script>
</html>

添加首页home.html

<!DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>
    <div sec:authorize="!isAuthenticated()">
    <a th:href="@{/toLogin}">登录</a>
    </div>

    <div sec:authorize="isAuthenticated()">
        用户名:<span sec:authentication="principal.username"></span>
        角色:<span sec:authentication="principal.authorities"></span>
        <a th:href="@{/logout}">注销</a>
    </div>
</div>
<div sec:authorize="hasRole('level1')">
<a th:href="@{/level1/page/1}">level1-page1</a>
<a th:href="@{/level1/page/2}">level1-page2</a>
<a th:href="@{/level1/page/3}">level1-page3</a>
</div>
<div sec:authorize="hasRole('level2')">
    <a href="/level2/page/1">level2-page1</a>
    <a href="/level2/page/2">level2-page2</a>
    <a href="/level2/page/3">level2-page3</a>
</div>
<div sec:authorize="hasRole('level3')">
    <a href="/level3/page/1">level3-page1</a>
    <a href="/level3/page/2">level3-page2</a>
    <a href="/level3/page/3">level3-page3</a>
</div>
</body>
</html>

配置springSecurity

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    

    @Autowired
    @Qualifier(value = "jdbcUserDetailService") //自定义的userDetailsService
    UserDetailsService userDetailsService; 

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
    
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    }

    //认证
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
    
        http.authorizeRequests()
                .antMatchers("/","/index","/toLogin","/login","/captcha/code").permitAll()
                .antMatchers("/level1/**").hasRole("level1")
                .antMatchers("/level2/**").hasRole("level2")
                .antMatchers("/level3/**").hasRole("level3");
        http.formLogin().loginPage("/toLogin")  //指定自定义的登录页面
                .usernameParameter("name")      //指定自定义的登录页面用户名
                .passwordParameter("passwd")    //指定自定义的登录页面密码
                .loginProcessingUrl("/login")//表单提交的路径
                .successHandler(new MySuccessHandler())  //登陆成功后处理
                .failureHandler(new MyFailureHandler()); //登陆失败后处理
        http.csrf().disable();
        //在UsernamePasswordAuthenticationFilter之前添加自定义的过滤器
        http.addFilterBefore(new LoginFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}
public class MySuccessHandler implements AuthenticationSuccessHandler {
    
    
    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
    
    
        httpServletResponse.setContentType("application/json;charset=utf-8");
        ServletOutputStream out = httpServletResponse.getOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.writeValue(out, Result.success("登录成功"));
        out.flush();
        out.close();
    }
}
public class MyFailureHandler implements AuthenticationFailureHandler {
    
    
    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
    
    
        httpServletResponse.setContentType("application/json;charset=utf-8");
        ServletOutputStream out = httpServletResponse.getOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();
        if ("验证码错误".equals(e.getMessage())){
    
      //是否是验证码错误,这个是自定义的异常信息VerifcatioinExeption中指定的内容
            objectMapper.writeValue(out, Result.fail("验证码错误,请重新输入"));
        }else{
    
    
            objectMapper.writeValue(out, Result.fail("登录失败"));
        }
        out.flush();
        out.close();
    }
}

自定义的异常信息

public class VerifcatioinExeption extends AuthenticationException {
    
    

    public VerifcatioinExeption() {
    
    
        super("验证码错误");
    }
}

自定义过滤器

public class LoginFilter extends OncePerRequestFilter {
    
    
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
    
    
        //只过滤登录的请求
        String requestURI = httpServletRequest.getRequestURI();
        if (!"/login".equals(requestURI)){
    
    
            filterChain.doFilter(httpServletRequest,httpServletResponse);
        }else {
    
    
            try {
    
    
                verifcatioinCode(httpServletRequest);
                filterChain.doFilter(httpServletRequest,httpServletResponse);
            }catch (VerifcatioinExeption e){
    
    
                new MyFailureHandler().onAuthenticationFailure(httpServletRequest,httpServletResponse,e);
            }
        }

    }
    //验证码是否正确
    public void verifcatioinCode(HttpServletRequest httpServletRequest){
    
    
        String requestyzm = httpServletRequest.getParameter("yzm");
        HttpSession session = httpServletRequest.getSession();
        Object code = session.getAttribute("code");
        String  sessionCode="";
        if(code!=null){
    
    
            sessionCode=(String) code;
        }

        if(!StringUtils.isEmpty(sessionCode)){
    
    
            //如果可以进入这段代码说明用户已经在登录页面看到验证码,这个验证码已经没有用了
            session.removeAttribute("code");
        }
        if(StringUtils.isEmpty(requestyzm) || StringUtils.isEmpty(sessionCode) || !requestyzm.equals(sessionCode)){
    
    
            //失败
            throw  new VerifcatioinExeption();
        }
    }
}

启动项目,登录系统
在这里插入图片描述
补充:自定义userDetailsService(这里有多种方式,请参考替换默认的userDetailsService

猜你喜欢

转载自blog.csdn.net/weixin_45742032/article/details/114124110