之前都是讲权限写死在配置config
中,现在将权限读取出来,并且加上图形验证码做登录检查,关于图形验证码很多,介绍起来就没啥意思了,根据上一个章节说的 图形验证码 整合在这个SpringSecurity
中 [自行参考] ,我们主要来看一下 SpringSecurity
是如何整合 这样一个图形验证的,整合之前,我们需要一个filter
拦截器,去拦截这样一个请求
VerificationCodeFilter
代码语言:javascript复制package com.shaojie.authority.filter;
import cn.hutool.core.util.StrUtil;
import com.shaojie.authority.exception.VerificationCodeException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author: ShaoJie
* @data: 2020年02月09日 16:40
* @Description: 图形验证码过滤器
* <p>
* OncePerRequestFilter 可以确保一次请求只会通过一次该过滤器 (Filter 在实际的操作中并不能保证这一点)
*/
@Slf4j
public class VerificationCodeFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 非登录请求不校验当前的值
if (!"/index".equals(request.getRequestURI())) {
// 放行
filterChain.doFilter(request, response);
} else {
verificationCode(request);
filterChain.doFilter(request, response);
}
}
public void verificationCode(HttpServletRequest request) throws VerificationCodeException {
// 后去验证表单的值 --> 图形验证码
String captcha = request.getParameter("captcha");
log.info("表单的验证码: {}",captcha);
// 取出 访问时 已经添加在 session 中的验证码
String sessionCaptcha = (String) request.getSession().getAttribute("captcha");
log.info("session的验证码: {}",sessionCaptcha);
// 判断两次的值是否值一样的
if (!StrUtil.isEmpty(sessionCaptcha)) {
// 清楚当前的验证码 无论是否成功或是失败 客户端登录失败应刷新当前的验证码
request.getSession().removeAttribute("captcha");
}
// 表单提交的验证码 session 中的验证码 两者均不能为空 且两者需一致
if (StrUtil.isEmpty(captcha) || StrUtil.isEmpty(sessionCaptcha) || !captcha.equals(sessionCaptcha)) {
// 这里有个问题 不管是任何时候都会校验这个图形验证码 需要判断一下请求的路径
throw new VerificationCodeException();
}
}
}
这里需要手动抛出验证异常 new VerificationCodeException()
代码语言:javascript复制package com.shaojie.authority.exception;
import javax.security.sasl.AuthenticationException;
/**
* @author: ShaoJie
* @data: 2020年02月09日 16:38
* @Description: 校验失败异常
* <p>
* 定义一个验证码校验失败的异常
*/
public class VerificationCodeException extends AuthenticationException {
public VerificationCodeException() {
super("图形验证码校验失败");
}
}
这里还需要说明一下关于从session
中获取这个验证码的问题,这里就是图形验证码一章中所提到的讲请求到的验证码保存到session
中,整合起来一起看,不然可能会很奇怪,这个session
中的验证码是怎么来的,还有一个就是表单的验证码,直接看一下登录表单吧
login.html
代码语言:javascript复制<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>登录页面</h2>
<form th:action="@{/index}" method="post">
账号:<input type="text" name="userName" value="1591313226@163.com"><br>
密码:<input type="password" name="password" value="123456"><br>
<img src="/captcha.jpg" alt="" th:width="150" height="50"><br>
验证码:<input type="text" name="captcha"><br>
<button type="submit">登录</button>
</form>
</body>
</html>
多加了一个对图形验证码的请求,将图片展示到页面上,每次请求都会重新请求一次这个图片。
后面 就是怎么和当前的这个SpringSecurity
整合了,我们只需要将当前的拦截器加入到这个登录拦截器之前即可,也就是在这个SpringSecurityConfig
配置当中,并且在这里的权限也将会从数据库读取,而不是写死了
权限范围实体 Purview
代码语言:javascript复制package com.shaojie.authority.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.persistence.*;
import java.io.Serializable;
/**
* @author: ShaoJie
* @data: 2020年02月10日 14:55
* @Description: 权限范围及路径
*/
@Data
@Entity
@AllArgsConstructor
@Accessors(chain = true)
@Table(name = "purview")
public class Purview implements Serializable {
private static final long serialVersionUID = 8025367145943417425L;
/**
* 路径自增 id
*/
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
/**
* 路径
*/
@Column(name = "url")
private String url;
/**
* 路径权限
*/
@Column(name = "authority")
private String authority;
public Purview() {
}
}
SpringSecurityConfig
配置
代码语言:javascript复制 /**
* 验证
*
* @param http
* @throws Exception
*/
// 代替配置文件 <security:http></security:http>
@Override
protected void configure(HttpSecurity http) throws Exception {
// 添加权限
selectPurview(http);
http.authorizeRequests()
// antMatchers 设置拦截的请求 hasAnyAuthority 对应的权限名称
// .hasAnyAuthority("PRODUCT_ADD") 用户所具有的权限
// 可替换成 .hasRole() 针对角色做验证
// .antMatchers("/product/add").hasAnyAuthority("PRODUCT_ADD")
// .antMatchers("/product/update").hasAnyAuthority("PRODUCT_UPDATE")
// .antMatchers("/product/list").hasAnyAuthority("PRODUCT_LIST")
// .antMatchers("/product/delete").hasAnyAuthority("PRODUCT_DELETE")
// permitAll 所有的权限都能访问
.antMatchers("/login").permitAll()
.antMatchers("/captcha.jpg").permitAll()
// .antMatchers("/**")
// fullyAuthenticated 不允许匿名用户查看
// .fullyAuthenticated()
// 设置所有的请求都必须经过验证才能访问
.anyRequest().authenticated()
.and()
// httpbasic 登录
// .httpBasic();
// 表单登录
.formLogin()
// 登录请求的页面
.loginPage("/login")
// 处理登录请求的 地址
.loginProcessingUrl("/index")
// 定义 故障处理器
// .failureHandler()
// 修改 spring 提供的 默认登陆参数
.usernameParameter("userName")
.passwordParameter("password")
.and()
// 开启记住我功能
.rememberMe()
.and()
// 开启登出
.logout()
.and()
//添加过滤器 将 过滤器添加在 UsernamePasswordAuthenticationFilter 之前 也就是在验证账号密码之前
.addFilterBefore(new VerificationCodeFilter(),
UsernamePasswordAuthenticationFilter.class)
// 禁用跨域的保护
.csrf().disable();
}
/**
* 查询权限并将权限放入 security 中
*
* @param http
* @throws Exception
*/
public void selectPurview(HttpSecurity http) throws Exception {
List<Purview> purviews = purviewService.selectPurview();
for (Purview purview : purviews) {
http.authorizeRequests()
.antMatchers(purview.getUrl()).hasAnyAuthority(purview.getAuthority());
}
}
因为使用的持久层框架是SpringDataJpa
所以,也需要修改一下关于用户的enable
(账户是否启动)字段,需要在验证登录那里做一下处理,
UserServiceImpl
代码语言:javascript复制 /**
* 根据用户的账号查询用户
*
* @param name 用户的账号
* @return 用户信息
*/
@Override
public User findUserByName(String name) {
Example<User> example = Example.of(new User()
.setName(name)
.setEnable(true));
return userRepository.findOne(example).get();
}
只有启用的用户才有登录的权限,否则不允许登录,这里根据不同的持久层做调整。
闲聊几句,最近发现这个底层的源码有点难消化,貌似还是没掌握到java
的精髓,有点难啃,只能慢慢来了!心急也吃不上热乎的!