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)