羽化登峰

导航

 
1.引入相关依赖

      <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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

2.SpringSecurity完整流程

  其中UsernamePasswordAuthenticationFilter负责处理页面填写了用户名密码后的登陆请求,验证是否正确,FilterSecurityInterceptor负责权限校验的过滤器,在没有Filter前后都可以加自定义拦截器。
3.自定义配置

@EnableWebSecurity
public class WebConfigUtil extends WebSecurityConfigurerAdapter {
  @Autowired
  MyWebAuthenicationDetailsSource detailsSource;
  @Autowired
  MyUserDetailsAuthenticationProvider provider;
  @Autowired
  MyJdbcUserDetailService myJdbcUserDetailService;
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    //URL 拦截注册器,获取所有请求 ,antMatchers()和regexMatchers()
    http.authorizeRequests()
        .antMatchers("/hello").hasAnyRole("xxz")//验证/hello接口必须拥有xxz权限
        .antMatchers("/word").hasRole("xx")
        .anyRequest().authenticated()//除permitAll()配置的接口,其他所有请求必须认证
      .and()
        .formLogin()//声明了需要Spring Security提供的表单认证,httpBasic()
          // 第二种方式:验证验证码,通过改写认证方式,默认DaoAuthenticationProvider,还需要配置 第四步
          .authenticationDetailsSource(detailsSource)//自定义封装form表单参数,如验证码
          .loginPage("/login.html")//指定登录页,获取resources/static/
          .loginProcessingUrl("/login")//前端展示路径,successHandler 处理登录请求的
          .successHandler(new AuthenticationSuccessHandler() {
            @Override
            public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
              HttpServletResponse httpServletResponse, Authentication authentication) throws
              IOException, ServletException {
                httpServletResponse.setContentType("application/json;charset=utf-8");
                PrintWriter out = httpServletResponse.getWriter();
                out.write("{\"error_code\":100}");
              }
          })
        .failureHandler(new AuthenticationFailureHandler() {
          @Override
          public void onAuthenticationFailure(HttpServletRequest httpServletRequest,
            HttpServletResponse httpServletResponse, AuthenticationException e) throws
            IOException, ServletException {
            }
        })
      .failureUrl("")
      .permitAll()//无权限认证
    .and()
      .rememberMe().userDetailsService(myJdbcUserDetailService)//添加自动登录功能,默认简单散列加密
      .key("key-cookie") //在cookie是一个map类型,如果不知道key,是个UUID随机值
      .tokenRepository(new JdbcTokenRepositoryImpl())//基于数据库做持久化
    .and()
      .logout()//注销登录
      .logoutUrl("/mylogout")//非业务代码中真实存在的路径
      .logoutSuccessUrl("/")//注销成功,重定向到该路经下
      .logoutSuccessHandler(new LogoutSuccessHandler() {
        @Override
        public void onLogoutSuccess(HttpServletRequest httpServletRequest,
           HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {

        }
      })
      .invalidateHttpSession(true)//使其用户HttpSession失效
      .deleteCookies()//删除cookie
    .and()
      .sessionManagement().sessionFixation().none()//一共4种模式
      .invalidSessionUrl("")//会话过期跳转路由,或者会话过期策略
      .maximumSessions(1)//最多可以同时登录一个
      .maxSessionsPreventsLogin(true)//是否剔除旧的登录,true阻止停止旧登录
    .and()
    .and()
      .csrf().disable();//跨站请求伪造防护功能
    //第一种方式验证验证码:在验证密码前添加自定义的拦截器
  http.addFilterBefore(new MyFilter(), UsernamePasswordAuthenticationFilter.class);
  // super.configure(http);//必须注释掉
}

4.实现WebSecurityConfigurerAdapter 第二个方法

  @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //第一种添加用户方式
       auth.inMemoryAuthentication().withUser("xx").password("oo").roles();
       // 第二种方式:验证验证码,配合上面自定义容器使用
       auth.authenticationProvider(provider);//自定义认证方式,可以实现多余的验证方式
    }

 

5.用户管理方式

  //第四步提供了一种方式,多种方式可以选择一种,数据来源可以是内存,也可以是数据库 
  /**
     *  第二种方式:基于内存
     * @return
     */
    @Bean
    public UserDetailsService userDetailsService2(){
        InMemoryUserDetailsManager manager =new InMemoryUserDetailsManager();
        User user =new User("a",
                new BCryptPasswordEncoder().encode("1"),true,
        true,true,true, Collections.singleton(new SimpleGrantedAuthority("xx")));
        manager.createUser(user);
        manager.createUser(User.withUsername("huang")
                .password(new BCryptPasswordEncoder().encode("xx")).roles("xxz").build());
        return manager;
    }
    /**
     *  第三种方式:基于默认数据库,org\springframework\security\core\userdetails\jdbc\users.ddl
     *
     * @return
     */
    //@Bean
    public UserDetailsService userDetailsService3(){
        JdbcUserDetailsManager manager =new JdbcUserDetailsManager();
        manager.createUser(User.withUsername("huang")//从数据库取出放入内存中,进行校验
                .password(new BCryptPasswordEncoder().encode("xx")).roles("xxz").build());
        return manager;
    }
    /**
     *  第四种方式:定义User对象:继承UserDetails, isAccountNonExpired、isAccountNonLocked 和 isCredentialsNonExpired 统一返回 true
     *  自定义的User要实现equels和hashCode方法,为了注册销毁内存中的对象
     *  继承UserDetailsService中
     * @return
     */
   //为第六步

6.基于自定义的数据库表创建的 

 

public class MyUser implements UserDetails {
    private Long id;
    private String username;
    private String password;
    private String roles;
    private boolean enable;
    private List<GrantedAuthority> authorities;
}
@Service
public class MyJdbcUserDetailService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
       // 实现从数据库读取用户信息
        MyUser dto=new MyUser();
        // 多权限用;分割
     dto.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(dto.getRoles()));
        return null;
    }
}

7.自定义认证过程
比如在验证用户密码前,验证验证码是否正确,还有一种方式是自定义Filter 

//补充用户提交的验证和session保持的验证码信息
public class MyWebAuthenticationDetails extends WebAuthenticationDetails {
    private boolean imageCodeIsRight;
    public boolean getImageCodeIsRight(){
        return this.imageCodeIsRight;
    }
    public MyWebAuthenticationDetails(HttpServletRequest request) {
        super(request);
        // 在这一步对用户提交的表单信息,获取,并与用户session中的信息比较,如短信验证码,并将比对结果放入
        this.imageCodeIsRight=true;
    }
}
@Component
public class MyUserDetailsAuthenticationProvider extends DaoAuthenticationProvider {
    public MyUserDetailsAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        this.setUserDetailsService(userDetailsService);
        this.setPasswordEncoder(passwordEncoder);
    }
    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        // 先完成完成图形验证
        super.additionalAuthenticationChecks(userDetails, authentication);
    }
}
@Component
public class MyUserDetailsAuthenticationProvider extends DaoAuthenticationProvider {
    public MyUserDetailsAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        this.setUserDetailsService(userDetailsService);
        this.setPasswordEncoder(passwordEncoder);
    }
    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        // 先完成完成图形验证
        MyWebAuthenticationDetails details= (MyWebAuthenticationDetails) authentication.getDetails();
        //将之前的结果取出
        details.getImageCodeIsRight();
        super.additionalAuthenticationChecks(userDetails, authentication);
    }
}

8.密码加密方式选择,针对数据库非明文存储 

//是否加密认证:NoOpPasswordEncoder 不加密
    //BCryptPasswordEncoder 非对称加密
    @Bean
    public PasswordEncoder passwordEncoder(){
        //return NoOpPasswordEncoder.getInstance();
        return new BCryptPasswordEncoder();
    }

9.application.properties配置

#会话过期时间
server.servlet.session.timeout= 60
<html>
<head>
<meta charset="utf-8">
</head>
<body>
登录
<form action="/login" method="post">
用户名:<input name="username" type="text"><br>
密码: <input name="password" type="password">
<br>
<!-- 验证码<input name="code" type="text"> <img src="http://localhost/kaptcha">-->
<br>
<!-- <input  th:name="${_csrf.parameterName}" th:value="${_csrf.token}"> -->
<button type="submit" >登录</button>
</form>
</body>
</html>

  

 

 

 


posted on 2022-05-12 16:06  默默攀岩  阅读(55)  评论(0)    收藏  举报