前后端不分离架构 -- SpringSecurity用法

**说明:本节介绍得是 前后端不分离结构 SpringSecurity整个流程基本用法。**

一、界面流程介绍:

1、访问首页展示:

首页:不进行拦截

2、每个按钮的访问权限不尽相同,如:"验证zs"按钮表示 登录用户必须拥有USER的访问权限才可访问,"验证li"按钮表示 登录用户必须拥有READ的访问权限才可访问。点击按钮时,未登录,则跳转登陆界面。

登录拦截界面

3、登录成功后,再次访问,便可访问成功。

在这里插入图片描述

4、访问其他无权限的按钮时,报无权限。

在这里插入图片描述
用户不同的权限,访问不同的按钮

二、代码描述

1、数据库表结构:

  1. 用户表:
    sys_user
  2. 角色表:
    sys_role
  3. 用户角色关联表:
    sys_user_role
  4. 还可添加权限表、角色_权限表

2、service层(只写服务接口,具体实现不做介绍)

/**
 * @Author: ckw
 * @description: 用户服务
 * @date: 2022/2/10 16:07
 */
public interface SysUserService {

    /**
     * @Author: ckw
     * @description: 根据用户名查询用户
     * @date: 2022/2/10 16:07
     * @params: 用户名
     * @return 用户
     */
    SysUser getSysUserInfo(String username);

    /**
     * @Author: ckw
     * @description: 保存用户
     * @date: 2022/2/10 16:07
     * @params: 用户
     * @return 是否成功
     */
    Boolean saveSysUser(SysUser sysUser);

}


/**
 * @Author ckw
 * @Date 2022/2/8 19:54
 * @Version 1.0
 * @description: 角色服务
 */
public interface SysRoleService {

    /**
     * @Author: ckw
     * @description: 根据用户id查询用户所拥有角色
     * @date: 2022/2/10 16:09
     * @params: 用户id
     * @return 用户对应角色 
     */
    List<SysRole> findRoleByUser(Integer userid);

}

3、Controller:

/**
 * @Author: ckw
 * @description: 验证码控制器
 * @date: 2022/2/10 15:00
 */
@Controller
@RequestMapping("/captcha")
public class CaptchaController {

    //定义一个值,用来生成验证码的图片
    //图像宽度 120像素
    private int width = 120;

    //图像高度 30 像素
    private int height = 30;

    //图片内容在图片的起始位置 12像素
    private  int drawY = 20;

    //文字的间隔  18像素
    private int space = 15;

    //验证码有个文字
    private int charCount = 6;

    //验证码的内容数组
    private String chars []= {"A","B","C","D","E","F",
    "G","H","I","J","K","L","M","N","O","P","T","U","V","W",
    "X","Y","Z","1","2","3","4","5","6","7","8","9","0"};



    //定义方法:生成验证码内容。 在一个图片上,写入文字
    @GetMapping("/code")
    public void makeCaptchaCode(HttpServletRequest request,HttpServletResponse response) throws IOException {

        /*
           验证码:需要在内存中绘制一个图片BufferedImage.
           向这个图片中写入文字。 把绘制好内容的图片响应给请求
         */

        //创建一个背景透明的图片,使用rgb表示颜色的
        BufferedImage image = new BufferedImage(width,height, BufferedImage.TYPE_INT_RGB);

        //获取画笔
        Graphics g  = image.getGraphics();

        //设置使用画笔是白颜色
        g.setColor(Color.white);

        //给image画板都涂成白色的
        // fillRect(矩形的起始x,矩形的起始y, 矩形的宽度,矩形的高度)
        g.fillRect(0,0,width,height);

        //画内容
        //创建一个字体
        Font font  = new Font("宋体",Font.BOLD,16);
        g.setFont(font);

        g.setColor(Color.black);
        //在画布上,写一个文字
        //参数: 文字,x,y坐标
        //g.drawString("中",10,drawY);

        StringBuffer buffer = new StringBuffer("");
        int ran = 0;
        int len = chars.length;
        for(int i=0;i<charCount;i++){
            ran  = new Random().nextInt(len);
            buffer.append(chars[ran]);
            g.setColor(makeColor());
            g.drawString(chars[ran],(i+1)*space,drawY);
        }

        //绘制干扰线
        for(int m=0;m<4;m++){
            g.setColor(makeColor());
            int dot [] = makeLineDot();
            g.drawLine(dot[0],dot[1],dot[2],dot[3]);
        }

        //把生成的验证码存储到session中
        request.getSession().setAttribute("code",buffer.toString());

        //设置没有缓冲
        response.setHeader("Pragma","no-cache");
        response.setHeader("Cache-Control","no-cache");
        response.setDateHeader("Expires",0);
        response.setContentType("image/png");
        OutputStream out = response.getOutputStream();
        /*
           RenderedImage im, 输出的图像
           String formatName, 图像的格式 jpg,jpeg, png
           ImageOutputStream output 输出到哪
         */
        ImageIO.write(image,"png",out);
        out.flush();
        out.close();

    }

    private Color makeColor(){
        Random random = new Random();
        int r = random.nextInt(255);
        int g = random.nextInt(255);
        int b = random.nextInt(255);

        return new Color(r,g,b);
    }

    private 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,y2};

    }
}

/**
 * @Author: ckw
 * @description: 首页
 * @date: 2022/2/10 15:00
 */
@Controller
public class IndexController {

    @GetMapping("/index")
    public String toIndexHtml(){
        return "forward:/index.html";
    }
}


/**
 * @Author: ckw
 * @description: 访问
 * @date: 2022/2/10 15:00
 */
@RestController
public class MyController {

    @GetMapping(value = "/access/user",produces = "text/html;charset=utf-8")
    public String sayUser(){
        return "zs 是 user 角色";
    }

    @GetMapping(value = "/access/read",produces = "text/html;charset=utf-8")
    public String sayRead(){
        return "lisi 是 read 角色";
    }

    @GetMapping(value = "/access/admin",produces = "text/html;charset=utf-8")
    public String sayAdmin(){
        return "admin 是 user , admin 角色";
    }
}

4、实现UserDetailsService 查询用户是否存在(在这里可以设置用户的权限)

/**
 * @Author ckw
 * @Date 2022/2/8 9:01
 * @Version 1.0
 * @description: 用户详情
 */
@Service
public class UserDetailServiceImpl implements UserDetailsService {

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private SysRoleService sysRoleService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SysUser sysUserInfo = sysUserService.getSysUserInfo(username);
        if(sysUserInfo != null){
            List<SysRole> sysRoles = sysRoleService.findRoleByUser(sysUserInfo.getId());
            List<GrantedAuthority> authorityList = new ArrayList<>();
            sysRoles.forEach(role ->{
                //角色必须以ROLE_开头
                GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_" +role.getName());
                authorityList.add(authority);
            });
            sysUserInfo.setAuthorities(authorityList);
        }
        return sysUserInfo;
    }
}

5、Security 核心配置类WebSecurityConfigurerAdapter

/**
 * @Author ckw
 * @Date 2022/2/7 17:09
 * @Version 1.0
 * @description: security控制管理器
 */
@Configuration //配置类
@EnableWebSecurity //启动security安全框架功能
//@EnableGlobalMethodSecurity(prePostEnabled = true) //开启基于方法的安全认证机制
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {

    //把SuccssHandler , FailureHandler注入进来
    @Autowired
    private AuthenticationSuccessHandler successHandler;

    @Autowired
    private AuthenticationFailureHandler failureHandler;

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        System.out.println("=======configure HttpSecurity========== ");
        http.authorizeRequests()
                //指定那些地址可以直接访问, 和登录有关的需要进行指定
                .antMatchers("/index","/myajax.html","/login","/js/**","/captcha/**").permitAll()
                .antMatchers("/access/user/**").hasRole("USER")
                .antMatchers("/access/read/**").hasRole("READ")
                .antMatchers("/access/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .successHandler(successHandler)
                .failureHandler(failureHandler)
                .loginPage("/myajax.html")  //登录的自定义视图页面
                .loginProcessingUrl("/login") //form中登录的访问uri地址
                .and()
                //关于跨域访问的安全设置,先禁用
                .csrf().disable();

        //在框架的过滤器链条中,增加一个自定义过滤器
        http.addFilterBefore(new VerificationCodeFilter(), UsernamePasswordAuthenticationFilter.class);


    }
}

6、登录成功拦截器、登录失败拦截器

/**
 * @Author ckw
 * @Date 2022/2/8 9:01
 * @Version 1.0
 * @description: 登录成功拦截器
 */
@Component
public class MySuccessHandler implements AuthenticationSuccessHandler {
    /*
       参数:
         request : 请求对象
         response:应答对象
         authentication: spring security框架验证用户信息成功后的封装类。
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
                                        HttpServletResponse response,
                                        Authentication authentication) throws IOException, ServletException {
        //登录的用户信息验证成功后执行的方法
        response.setContentType("text/json;charset=utf-8");

        Result result = new Result();
        result.setCode(0);
        result.setError(1000);
        result.setMsg("登录成功");

        OutputStream out = response.getOutputStream();
        ObjectMapper om = new ObjectMapper();
        om.writeValue(out,result);
        out.flush();
        out.close();


    }
}


/**
 * @Author: ckw
 * @description: 登录失败拦截器
 * @date: 2022/2/10 15:04
 */
@Component
public class MyFailureHandler implements AuthenticationFailureHandler {

    private Result result;

    public Result getResult() {
        return result;
    }

    public void setResult(Result result) {
        this.result = result;
    }

    /*
           参数:
             request : 请求对象
             response:应答对象
             authentication: spring security框架验证用户信息成功后的封装类。
         */
    @Override
    public void onAuthenticationFailure(HttpServletRequest request,
                                        HttpServletResponse response,
                                        AuthenticationException e) throws IOException {
        //当框架验证用户信息失败时执行的方法
        response.setContentType("text/json;charset=utf-8");

        if( result == null){
            Result  localResult  = new Result();
            localResult.setCode(1);
            localResult.setError(1001);
            localResult.setMsg("登录失败");
            result = localResult;
        }


        OutputStream out = response.getOutputStream();
        ObjectMapper om = new ObjectMapper();
        om.writeValue(out,result );
        out.flush();
        out.close();

    }
}

7、验证码拦截器(只拦截登录接口)

/**
 * @Author ckw
 * @Date 2022/2/8 9:01
 * @Version 1.0
 * @description: 验证码拦截器
 */
public class VerificationCodeFilter extends OncePerRequestFilter {

    private MyFailureHandler failureHandler = new MyFailureHandler();
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {

        System.out.println("VerificationCodeFilter  doFilterInternal ");
        //只有是login操作,才需要这个过滤器参与验证码的使用
        String uri = request.getRequestURI();
        if( !"/login".equals(uri)){
            //过滤器正常执行,不参与验证码操作
            filterChain.doFilter(request,response);
        } else {
            //登录操作,需要验证code
            try{
                //验证:code是否正确
                verifcatioinCode(request);
                //如果验证通过,过滤器正常执行
                filterChain.doFilter(request,response);

            }catch (VerificationException e){
                Result result  = new Result();
                result.setCode(1);
                result.setError(1002);
                result.setMsg("验证码错误!!!");
                failureHandler.setResult(result);
                failureHandler.onAuthenticationFailure(request,response,e);
            }
        }
    }

    private void verifcatioinCode(HttpServletRequest request){
        HttpSession session = request.getSession();
        //获取请求中的code
        String requestCode = request.getParameter("code");
        //获取session中的code
        String sessionCode = "";

        Object attr = session.getAttribute("code");
        if(attr !=null ){
            sessionCode = (String)attr;
        }
        System.out.println("VerificationCodeFilter  doFilterInternal requestCode:"+requestCode+"|sessionCode:"+sessionCode);
        //处理逻辑
        if(!StringUtils.isEmpty(sessionCode)){
            //在session中的code, 用户看到这个code了。
            //如果能到这段代码,说明用户已经发起了登录请求的。
            //session中的现在的这个code就应该无用
            session.removeAttribute("code");
        }

        //判断code是否正确。
        if( StringUtils.isEmpty(requestCode) ||
                StringUtils.isEmpty(sessionCode) ||
                !requestCode.equals(sessionCode) ){
            //失败
            throw new VerificationException();
        }
    }
}

8、响应类

/**
 * @Author: ckw
 * @description: 结果
 * @date: 2022/2/10 15:00
 */
public class Result {
    // code=0 成功; code =1 失败
    private int code;
    //表示错误码
    private int error;
    //消息文本
    private String msg;

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public int getError() {
        return error;
    }

    public void setError(int error) {
        this.error = error;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

三、整体走向:

  1. MyWebSecurityConfig security控制管理器配置参数
  2. 用户登录时,先验证码拦截器拦截校验VerificationCodeFilter.doFilterInternal()
  3. 登录时,查询数据库UserDetailServiceImpl.loadUserByUsername()方法,对用户进行验证
  4. 验证成功:MySuccessHandler.onAuthenticationSuccess();验证失败:MyFailureHandler.onAuthenticationFailure()。

四、小点

  1. 可以设置登录无操作有效期:
    在这里插入图片描述
    在这里插入图片描述
  2. 可以自己实现token,进行校验
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
jeesuite-libs分布式架构开发套件。包括缓存(一二缓存、自动缓存管理)、队列、分布式定时任务、文件服务(七牛、阿里云OSS、fastDFS)、日志、搜索、代码生成、API网关、配置中心、统一认证平台、分布式锁、分布式事务、集成dubbo、spring boot支持、统一监控等。所有release版都经过严格测试并在生产环境稳定运行4年+。 功能列表: cache模块 基于配置支持单机、哨兵、分片、集群模式自由切换 更加简单的操作API封装 一缓存支持(ehcache & guava cache)、分布式场景多节点自动通知 多组缓存配置同时支持 (一个应用多个redis server) 分布式模式开关 kafka模块 基于spring封装简化配置和调用方式 基于配置新旧两版Consumer API兼容支持 支持二阶段处理,即:fetch线程同步处理和process线程异步处理 消费成功业务处理失败自动重试或自定义重试支持 process线程池采用LinkedTransferQueue,支持线程回收和队列大小限制,确保系统崩溃等不会有数据丢失。 支持特殊场景发送有状态的消息(如:同一个用户的消息全部由某一个消费节点处理) producer、consumer端监控数据采集,由(jeesuite-admin)输出 兼容遗留kafka系统、支持发送和接收无封装的消息 mybatis模块 代码生成、自动CRUD、可无缝对接mybaits增强框架Mapper 基于properties配置多数据源支持,无需修改XML 读写分离,事务内操作强制读主库 基于注解自动缓存管理(所有查询方法结果自动缓存、自动更新,事务回滚缓存同步回滚机制) 自动缓存实现基于jeesuite-cache和spring-data-redis 分页组件 敏感操作拦截 scheduler模块 支持分布式保证单节点执行(按节点平均分配job) 支持failvoer,自动切换故障节点 支持多节点下并行计算 支持无注册中心单机模式 支持自定义重试策略 支持配置持久化(启动加载、变更保存) 支持控制台(jeesuite-admin)任务监控、开停、动态修改调度时间策略、手动触发执行 jeesuite-security 配置简单(初始化一个类即可) 满足认证授权基本需求 更加贴近日常使用业务场景 可选本地session和共享session 可选是否支持多端同时登录 dubbo、springboot跨服务登录状态传递支持 rest模块 自动resonse封装(xml、json) i18n request、response日志记录 自动友好错误 校验框架 filesystem模块 七牛文件服务支持 阿里云OSS文件服务支持 fastDFS文件系统支持 支持spring集成 配置式切换服务提供商 common模块 一些常用工具类 common2模块(需要依赖一些组件或者基础设置) 分布式锁 分布式全局ID生成器 excel导入导出 jeesuite-springboot-starter模块 springboot集成支持

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值