SpringSecurity入坑(四)

2021-08-09 17:36:39 浏览数 (1)

之前都是讲权限写死在配置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的精髓,有点难啃,只能慢慢来了!心急也吃不上热乎的!

0 人点赞