FormLogin

添加依赖

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

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

1 官方默认操作

登录地址:

spring security 用户名密码认证 springsecurity默认用户名密码_html

密码:

spring security 用户名密码认证 springsecurity默认用户名密码_spring_02

2 自定义账号密码

通过源码可以知道,我们可以去实现该接口,返回自定义的包含密码和权限的User对象。springsecurity会在其他地方拿出该User对象的password去跟前端传来的password做比对,比对成功则通过验证

spring security 用户名密码认证 springsecurity默认用户名密码_spring_03

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //我们自定义的用户名是“xxjqr”
        if (!Objects.equals("xxjqr", username)) {
            //抛出springsecurity自带的N多异常对象
            throw new UsernameNotFoundException("用户名不匹配");
        }

        //String password = new BCryptPasswordEncoder().encode("1234");
        //这里我们先不从数据库中取密码,我们使用自定义的
        String password = passwordEncoder.encode("1234");

        //真实开发中应该从数据库中提取用户权限和角色,这里我们直接构造normal权限和admin的角色。角色默认的前缀都是ROLE_
        return new User(username,password, AuthorityUtils.commaSeparatedStringToAuthorityList("low,ROLE_admin"));
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        //使用BCrypt加密机制
        return new BCryptPasswordEncoder();
    }
}

到这里我们已经可以自定义登录的账号密码了,但是登录成功并没有显示其他东西,因为官方貌似就给了一个登录页面而已

3 认证授权逻辑自定义配置

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //super.configure(http);

        //自定义form表单的登录页面,和登录的接口;
        http.formLogin().loginPage("/login.html")
                //这个接口是不需要我们写的,是spring去代理的,但是我们可以自定义登陆逻辑,这个路径必须和form表单组件的action路径一致
                .loginProcessingUrl("/login")
                // 登录成功后跳转路径,为啥不直接写home.html呢,因为security帮我们跳转是使用的post请求,所以我们要用接口去手动跳转
                //.successForwardUrl("/home")
                .defaultSuccessUrl("/home.html")
                // 这个方式不生效,有待研究
                //.failureForwardUrl("/error");
                .failureUrl("/error.html");

        /*这里做个小总结,ForwardUrl都是使用post方式去请求地址
          所以successForwardUrl,failureForwardUrl,都需要后端
          去提供接口再做页面跳转,并且failureForwardUrl还不生效*/

        //授权
        http.authorizeRequests()
                //登录页面和错误页面要开放,成功页面是登录成功后的,所以有权限。开放多个用逗号隔开“a”,"b"
                .antMatchers("/login.html","/error.html").permitAll()
                //但是其他所有请求必须登录
                .anyRequest().authenticated();

        //关闭csrf防火墙
        http.csrf().disable();
    }
}

到目前为止里面大概内容就是:

1 自定义登录页面

2 自定义登录时对账号密码的校验

3 定义哪些url拦截,哪些不拦截

4 定义登陆成功和失败后的页面跳转

4 权限控制细化

4.1 功能清单

1 自定义handler处理登陆成功、失败、无权限跳转

2 针对不同的请求方式的方法做拦截

3 正则表达式的方式过滤

4 ip过滤

5 直接通过access方法做过滤控制

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Resource
    private MyAccessDeniedHandler accessDeniedHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //super.configure(http);


        http.formLogin().loginPage("/login.html")
                .loginProcessingUrl("/login")
                //只用自定义的handler来处理登录成功,失败的逻辑
                .successHandler(new MySuccessHandler("/home.html"))
                .failureHandler(new MyFailureHandler("/error.html"));

        //授权
        http.authorizeRequests()
                .antMatchers("/login.html", "/error.html").permitAll()
                //对权限做过滤控制
                .antMatchers("/girl.html").hasAnyAuthority("normal")
                //对角色做过滤控制
                .antMatchers("/girl.html").hasAnyRole("admin")
                .anyRequest().authenticated();


                // 不同方式的请求方法做拦截
                // antMatchers()和.regexMatchers()
                //.antMatchers(HttpMethod.DELETE,"/index.html")

                // 针对ip做过滤
                //.antMatchers("").hasIpAddress(“”)

                // 通过access方法来处理,因为其他权限控制方法底层都是调用的access
                //.antMatchers("").access("hasAnyRole('admin')")

        //当登录成功后,访问到没有授权的资源时
        http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);

        //关闭csrf防火墙
        http.csrf().disable();
    }
}

MySuccessHandler

public class MySuccessHandler implements AuthenticationSuccessHandler {

    private String url;

    public MySuccessHandler(String url) {
        this.url = url;
    }


    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {

        User user = (User)authentication.getPrincipal();
        System.out.println(user.getUsername());
        System.out.println(user.getPassword());
        System.out.println(user.getAuthorities());

        httpServletResponse.sendRedirect(url);
    }
}

MyAccessDeniedHandler

@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        //重定向跳转
        httpServletResponse.sendRedirect("/403.html");
    }
}

MyFailureHandler

public class MyFailureHandler implements AuthenticationFailureHandler {

    private String url;

    public MyFailureHandler(String url) {
        this.url = url;
    }

    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        System.out.println("密码错误了");
        httpServletResponse.sendRedirect(url);
    }
}
4.2 界面展示

spring security 用户名密码认证 springsecurity默认用户名密码_html_04

登陆成功后跳到 home.html

spring security 用户名密码认证 springsecurity默认用户名密码_spring_05

从上面的代码中我们知道 girl.html 资源需要 normal权限 或者 admin 角色

spring security 用户名密码认证 springsecurity默认用户名密码_spring_06

登陆的用户 xxjqr 具有 low 权限 和 admin角色

spring security 用户名密码认证 springsecurity默认用户名密码_html_07

所以点击 看美女 可以访问

spring security 用户名密码认证 springsecurity默认用户名密码_自定义_08

当我们在代码中把 xxjqr 的 admin角色删除后,再点击 看美女

spring security 用户名密码认证 springsecurity默认用户名密码_自定义_09

5 自定义权限校验

定义自定义权限的类和方法

@Service
public class MyServiceImpl {
    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        //获取主体
        Object obj = authentication.getPrincipal();
        //判断主体是否是UserDetails类型
        if (obj instanceof UserDetails) {
            //获取权限
            UserDetails userDetails = (UserDetails) obj;
            Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
            //判断请求的uri是否在权限里
            return authorities.contains(new SimpleGrantedAuthority(request.getRequestURI()));
        }
        return false;
    }
}

然后自定义配置就变成了

spring security 用户名密码认证 springsecurity默认用户名密码_html_10

此时,当我们想访问"/home.html"时,那用户必须要有改权限

spring security 用户名密码认证 springsecurity默认用户名密码_spring_11

Post、Json方式登录