SpringSecurity学习汇总

SpringSecurity学习汇总

在使用SpringSecurity时,首先需要添加Spring Security依赖,添加了这个依赖,就默认开启了认证授权。依赖如下

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

使用了这个依赖后,访问页面,就会默认跳到框架给的登录页面(当然也可以自己设置):http://localhost:8080/login。当你对SpringSecurity什么都没有配置的时候,默认用户username 是user,密码会在控制台生成(一个临时的uuid)

1. 自定义登录

1.1单个用户登录

在application.yml配置里一个用户

spring:
  security:
    user:
      name: zhangSan
      password: 123456

1.2多个用户登录

用户登录,密码需要加密,转成密文形式
使用User.builder()来构建UserDetails对象

@Configuration
public class SecurityConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {

		// 配置用户方式一
        UserDetails userDetails = User.builder()
                        .username("liSi")
                        .password(passwordEncoder().encode("123456"))       // 使用加密器加密
                        .build();       // 推荐使用构造器模式
        // 配置用户方式二,注意此时需要加一个权限值
        UserDetails userDetails1 = new User("wangWu", passwordEncoder().encode("123456"), AuthorityUtils.NO_AUTHORITIES);

        return new InMemoryUserDetailsManager(userDetails, userDetails1);
    }

}

1.3DB登录

因为数据库的数据时密文的,所以不需要进行加密

    @Bean
    public UserDetailsService userDetailsService() {
        return new UserDetailsService() {
            @Override
            public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                TUser tUser = tUserMapper.selectByName(username);
                UserDetails userDetails = User.builder()
                        .username(tUser.getUsername())
                        .password(tUser.getPassword())
                        .build();
                return userDetails;
            }
        };
    }

2.自定义登录页

注意:在实际开发中跨站请求伪造的验证尽量保持开启状态,保证在进行敏感操作时的安全性

@Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.formLogin((formLogin)->formLogin.loginPage("/toLogin")      // 登录跳转到该地址
                        .loginProcessingUrl("/index")       
                        .successForwardUrl("/api/hello")      // 登录成功跳转到/api/hello地址,采用post请求
                )

                .authorizeHttpRequests((authorize)->authorize
                                .requestMatchers("/toLogin", "/test").permitAll()   // 这两个地址不用登录就能访问
                                .anyRequest().authenticated()   // 任何请求都需要认证(登录)才能访问
                )

                .csrf((csrf)->{
                    csrf.disable();     // 禁用跨站请求伪造的验证
                })

                .build();
    }

3.登录时进行验证码验证

生成一个验证码

@Controller
public class CaptchaController {

    @GetMapping(value = "/api/captcha")
    public void captcha(HttpServletRequest request, HttpServletResponse response) {
//        ICaptcha iCaptcha = CaptchaUtil.createCircleCaptcha(100, 25, 4, 16);
//        ICaptcha iCaptcha = CaptchaUtil.createLineCaptcha(100, 25, 4, 16);
//        ICaptcha iCaptcha = CaptchaUtil.createGifCaptcha(100, 25, 3);
//        生成干扰线的图形验证码
        ICaptcha iCaptcha = CaptchaUtil.createShearCaptcha(100, 25, 4, 2);

//        把验证码放入session
        request.getSession().setAttribute("captcha", iCaptcha.getCode());

        ServletOutputStream outputStream = null;

        try {
            outputStream = response.getOutputStream();
//            把验证码写出到浏览器客户端
            iCaptcha.write(outputStream);
//            刷新输出流的缓冲区
            outputStream.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

从服务器端引用验证码到客户端

<!--跳转地址需要和指定登录的处理地址一样-->
<form th:action="@{ '/index' }" method="post">
    用户姓名:<input name="loginAct"><br>
    用户密码:<input type="password" name="loginPwd"><br>
    <input name="captcha">
    <img id="captchaCode" th:src="@{ '/api/captcha' }" style="cursor: pointer" onclick="reImg()"><br>
    <input type="submit" value="登录">
</form>
// 点击验证码后进行验证码刷新
<script th:inline="javascript">
    function reImg() {
        var img = document.getElementById("captchaCode");
        img.src = "/api/captcha?random=" + Math.random();
    }
</script>

自定义对图形进行验证的Filter
对输入的验证码进行验证

@Component
public class CaptchaFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        System.out.println("---------------" + request.getRequestURI());
        if (request.getRequestURI().equals("/index")) {
            String captcha = request.getParameter("captcha");

            if (!StringUtils.hasText(captcha)) {
                throw new RuntimeException("验证码为空");
            }

            String sessionCaptcha = (String) request.getSession().getAttribute("captcha");

            if (!captcha.equalsIgnoreCase(sessionCaptcha)) {
                throw new RuntimeException("验证码不匹配");
            }

            filterChain.doFilter(request, response);
        } else {
            filterChain.doFilter(request, response);
        }
    }
}

在 Spring Security 的过滤器链中添加一个自定义的过滤器 captchaFilter,并将其放置在 UsernamePasswordAuthenticationFilter 类之前。

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
    return httpSecurity.formLogin((formLogin)->formLogin.loginPage("/toLogin")      // 登录跳转到该地址
                    .loginProcessingUrl("/index")       // 指定登录的处理地址
                    .successForwardUrl("/api/hello")      // 登录成功跳转到/api/hello地址,采用post请求
            )

            .addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class)

            .build();
}

4.登录时进行身份验证

4.1:基于角色的权限管理

方式一:直接在SecurityFilterChain 配置

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
    return httpSecurity
//                弹窗的登录框
            .httpBasic(httpBasis->{})

            .authorizeHttpRequests(authorize->
                    authorize
                            .requestMatchers("/api/add").hasAnyRole("admin", "dev")
                            .requestMatchers("/api/delete").hasRole("admin")
                            .requestMatchers("/api/update").hasAnyRole("admin", "dev")
                            .requestMatchers("/api/query").hasAnyRole("admin", "dev", "test")
                            .anyRequest().authenticated()   // 其他所有的请求都需要登录才能进行
                    )

//                没有权限访问时,跳转到403.html页面
            .exceptionHandling(exception->
                            exception.accessDeniedPage("/403.html")
                    )

            .build();

}

requestMatchers(“/api/add”).hasAnyRole(“admin”, “dev”)

这行代码表示admin用户和dev用户有权限访问"默认地址//api/add"页面,其他用户都没有权限访问这个页面
方式二:使用注解的形式配置

@RestController
public class RoleController {

    @GetMapping(value = "/")
    public String hello() {
        return "欢迎登录";
    }

    @PreAuthorize(value = "hasAnyRole('admin', 'dev')")
    @RequestMapping(value = "/api/add")
    public String add() {
        return "add";
    }

    @PreAuthorize(value = "hasRole('admin')")
    @RequestMapping(value = "/api/delete")
    public String delete() {
        return "delete";
    }

    @PreAuthorize(value = "hasAnyRole('admin', 'dev')")
    @RequestMapping(value = "/api/update")
    public String update() {
        return "update";
    }

    @PreAuthorize(value = "hasAnyRole('admin', 'dev', 'test')")
    @RequestMapping(value = "/api/query")
    public String select() {
        return "query";
    }

}

4.2基于资源的权限认证

方式一:直接在SecurityFilterChain 配置

@Bean
public UserDetailsService detailsService(PasswordEncoder passwordEncoder) {

    UserDetails adminUser = User.builder()
            .username("admin")
            .password(passwordEncoder.encode("123456"))
            .authorities("user:add", "user:delete", "user:update", "user:query")    // 配置用户的权限标识符
            .build();

    UserDetails devUser = User.builder()
            .username("dev")
            .password(passwordEncoder().encode("123456"))
            .authorities("user:add", "user:update", "user:query")
            .build();

    UserDetails testUser = User.builder()
            .username("test")
            .password(passwordEncoder().encode("123456"))
            .authorities("user:query")
            .build();

    return new InMemoryUserDetailsManager(adminUser, devUser, testUser);
}

authorities(“user:add”, “user:delete”, “user:update”, “user:query”)

这行代码表示,用户admin有权限访问上面四个地址

方式二:使用注解的形式配置

@RestController
public class ResourceController {

    @GetMapping(value = "/")
    public String hello() {
        return "欢迎登录";
    }

    @GetMapping(value = "/api/add")
    @PreAuthorize(value = "hasAuthority('user:add')")
    public String add() {
        return "add";
    }

    @GetMapping(value = "/api/delete")
    @PreAuthorize(value = "hasAuthority('user:delete')")
    public String delete() {
        return "delete";
    }

    @GetMapping(value = "/api/update")
    @PreAuthorize(value = "hasAuthority('user:update')")
    public String update() {
        return "update";
    }

    @GetMapping(value = "/api/query")
    @PreAuthorize(value = "hasAuthority('user:query')")
    public String query() {
        return "query";
    }

}

5.获取当前登录用户的信息的四种方式

@RequestMapping(value = "/api/main")
public String main(Model model, Principal principal) {
    model.addAttribute("principal", principal);
    return principal;
}

@RequestMapping(value = "/api/main2")
public @ResponseBody Object main2(Authentication authentication) {
    return authentication;
}

@RequestMapping(value = "/api/main3")
public @ResponseBody Object main3(UsernamePasswordAuthenticationToken authenticationToken) {
    return authenticationToken;
}

@RequestMapping(value = "/api/main4")
public @ResponseBody Object main4() {
    //从spring security的上下文环境中拿到当前登录人的信息对象
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    return authentication;
}
  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

费卡拉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值