美文网首页SpringBoot学习认证授权
SpringBoot中使用Spring Security

SpringBoot中使用Spring Security

作者: weisen | 来源:发表于2018-12-27 14:58 被阅读0次

    Spring Security 基本介绍

    这里就不对Spring Security进行过多的介绍了,具体的可以参考官方文档
    我就只说下SpringSecurity核心功能:

    • 认证(你是谁)
    • 授权(你能干什么)
    • 攻击防护(防止伪造身份)

    基本环境搭建

    这里我们以SpringBoot作为项目的基本框架,我这里使用的是maven的方式来进行的包管理,所以这里先给出集成Spring Security的方式

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>
    
           <!-- thymeleaf模板 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
    

    然后建立一个Web层请求接口

    @Controller
    public class IndexController {
    
        /**
         * 跳转首页
         */
        @GetMapping("")
        public void index1(HttpServletResponse response){
            //内部重定向
            try {
                response.sendRedirect("/index");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
      
        /**
         * 首页
         */
        @RequestMapping("/index")
        @ResponseBody
        public String index() {
            return "index";
        }
    }
    

    接下来可以直接进行项目的运行,并进行接口的调用看看效果了。

    通过网页的调用

    我们首先通过浏览器进行接口的调用,直接访问http://localhost:8080/index,如果接口能正常访问,那么应该显示“index”。
    但是我们是没法正常访问的,出现了下图的身份验证输入框

    image.png

    这是因为在SpringBoot中,默认的Spring Security就是生效了的,此时的接口都是被保护的,我们需要通过验证才能正常的访问。 Spring Security提供了一个默认的用户,用户名是user,而密码则是启动项目的时候自动生成的。
    我们查看项目启动的日志,会发现如下的一段Log

    Using default security password: 62ccf9ca-9fbe-4993-8566-8468cc33c28c

    当然你看到的password肯定和我是不一样的,我们直接用user和启动日志中的密码进行登录。

    登录成功后,就跳转到了接口正常调用的页面了。
    如果不想一开始就使能Spring Security,可以在配置文件中做如下的配置:

    # security 使能
    security.basic.enabled = false
    

    刚才看到的登录框是SpringSecurity是框架自己提供的,被称为httpBasicLogin。显示它不是我们产品上想要的,我们前端一般是通过表单提交的方式进行用户登录验证的,所以我们就需要自定义自己的认证逻辑了。

    改造1 使用页面表单登录

    1. 前端写一个登陆页面(使用 thymeleaf 模板引擎),login_page.html文件:
    <!DOCTYPE html>
    <html id="ng-app" ng-app="app"  xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8"/>
        <title>home</title>
    </head>
    <body>
    <form  class="form-signin" action="/form" method="post">
        <h2 class="form-signin-heading">用户登录</h2>
        <table>
            <tr>
                <td>用户名:</td>
                <td><input type="text" name="username"  class="form-control"  placeholder="请输入用户名"/></td>
            </tr>
            <tr>
                <td>密码:</td>
                <td><input type="password" name="password"  class="form-control" placeholder="请输入密码" /></td>
            </tr>
            <tr>
    
                <td colspan="2">
                    <button type="submit"  class="btn btn-lg btn-primary btn-block" >登录</button>
                </td>
            </tr>
        </table>
    </form>
    </body>
    </html>
    

    写一个controller方法指向该登陆页面,不能使用@RestController@ResponseBody,否则就返回字符串了。

     @RequestMapping("/loginPage")
        public String login() {
            return "login_page";
        }
    

    还需要配置上:

    spring:
      # 定位模板的目录,给返回的页面添加后缀名
      thymeleaf:
        prefix: classpath:/templates/
        suffix: .html
        servlet:
          content-type: text/html
        mode: HTML5
    
    1. 添加一个类 SecurityConfig 继承 WebSecurityConfigurerAdapter
      重写configure方法,并加上@Configuration 和@EnableWebSecurity 2个注解。
    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
         @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    // 关闭csrf防护
                    .csrf().disable()
                    .headers().frameOptions().disable()
                    .and();
            http
                    //登录处理
                    .formLogin() //表单方式,或httpBasic
                    .loginPage("/loginPage")
                    .loginProcessingUrl("/form")
                    .defaultSuccessUrl("/index") //成功登陆后跳转页面
                    .failureUrl("/loginError")
                    .permitAll()
                    .and();
            http
                    .authorizeRequests() // 授权配置
                    //无需权限访问
                    .antMatchers( "/css/**", "/error404").permitAll()
                    .antMatchers("/user/**").hasRole("USER")
                    //其他接口需要登录后才能访问
                    .anyRequest().authenticated()
                    .and();
        }
      
    }
    
    

    与/ css / **和/ error404匹配的请求是完全可访问的
    与/ user / **匹配的请求要求用户进行身份验证,并且必须与USER角色相关联
    使用自定义登录页面和失败URL启用基于表单的身份验证
    loginPage("/login")表示登录时跳转的页面,因为登录页面我们不需要登录认证,所以我们需要添加 permitAll() 方法。

    login-page 自定义登录页url,默认为/login
    login-processing-url 登录请求拦截的url,也就是form表单提交时指定的action
    failureUrl=表示登录出错的页面,我们可以简单写个提示:如 用户名或密码错误。

    .csrf().disable() 说明:Spring Security4默认是开启CSRF的,所以需要请求中包含CSRF的token信息,这里不添加这段代码的话会出现异常,加上的话可以关闭csrf(关闭后有安全漏洞)。

    测试:
    1、输入网址:http://127.0.0.1:8081/index,自动跳转到:http://127.0.0.1:8081/loginPage,返回登陆页面
    2、输入账号密码:错误的话返回http://127.0.0.1:8081/loginError,登陆失败页面
    正确的话:返回http://127.0.0.1:8081/index,登陆成功页面

    改造2、自定义用户名和密码

    1、用户直接写入内存中

    很显然,这样改造之后,虽然登录页面是好看了,但还远远不能满足我们的应用需求,所以第二步,我们改造自定义的用户名和密码。
    自定义用户名和密码有2种方式,一种是在代码中写死,这也是官方的demo,另一种是使用数据库
    首先是第一种:如

        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
    @Override
        public void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth
                    .inMemoryAuthentication()
                    .passwordEncoder(passwordEncoder())
                    .withUser("admin").password(passwordEncoder().encode("123456")).roles("ADMIN")
                    .and()
                    .withUser("test").password(passwordEncoder().encode("test123")).roles("USER");
    
        }
    

    新的版本需要对密码进行加密,不然会报错,所以这里要使用passwordEncoder,程序运行起来,这时用我们自己的用户名和密码 输入 admin 和123456 就可以了。
    你也可以多几个用户,就多几个withUser即可。

    .and().withUser("test").password("test123").roles("ADMIN");
    这样我们就有了一个用户名为test,密码为test123的用户了。

    2、使用数据库的用户

    第一种的只是让我们体验了一下Spring Security而已,我们接下来就要提供自定义的用户认证机制及处理过程。
    在讲这个之前,我们需要知道spring security的原理,spring security的原理就是使用很多的拦截器对URL进行拦截,以此来管理登录验证和用户权限验证

    用户登陆,会被AuthenticationProcessingFilter拦截,调用AuthenticationManager的实现,而且AuthenticationManager会调用ProviderManager来获取用户验证信息(不同的Provider调用的服务不同,因为这些信息可以是在数据库上,可以是在LDAP服务器上,可以是xml配置文件上等),如果验证通过后会将用户的权限信息封装一个User放到spring的全局缓存SecurityContextHolder中,以备后面访问资源时使用。

    所以我们要自定义用户的校验机制的话,我们只要实现自己的AuthenticationProvider就可以了。在用AuthenticationProvider 这个之前,我们需要提供一个获取用户信息的服务,实现 UserDetailsService 接口

    用户名密码->(Authentication(未认证) -> AuthenticationManager ->AuthenticationProvider->UserDetailService->UserDetails->Authentication(已认证)

    了解了这个原理之后,我们就开始写代码

    UserDetails接口

    UserDetailsService接口

    实现 UserDetailsService接口 来返回User的对象实例

    @Component
    public class MyUserDetailsService implements UserDetailsService {
        @Autowired
        private SysUserService userService;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            //这里可以可以通过username(登录时输入的用户名)然后到数据库中找到对应的用户信息,并构建成我们自己的UserInfo来返回。
            SysUser sysUser = userService.findUserByName(username);
            //由于权限参数不能为空,所以这里先使用AuthorityUtils.commaSeparatedStringToAuthorityList方法模拟一个admin的权限,该方法可以将逗号分隔的字符串转换为权限集合。
    //数据库中的密码是加密后的
            return new User(username, sysUser.getPassword(),AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
        }
    }
    

    一般而言登录的数据在protected void configure(AuthenticationManagerBuilder auth)中,所有需要修改SecurityConfig类里面的configure(AuthenticationManagerBuilder auth)方法

        @Override
        public void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth
                    //用户认证处理
                    .userDetailsService(userDetailsService)
                    //密码处理
                    .passwordEncoder(passwordEncoder());
        }
    

    重新运行项目,这时候就可以使用数据库的账号和密码登陆了。

    一般而言到这里就可以实现登陆验证了。但是遇到一个问题,这样的话用户名和密码都是定死的,我们拿不到form-data数据,如果因为前端的问题,这种密码登录方式以外,我们还要稍微修改提交给我们的form-data中的密码数据,做一下处理,自定义一个登录呢。这个时候就需要用到AuthenticationProvider了。

    AuthenticationProvider接口

    这是一个接口,提供了两种方法

    public interface AuthenticationProvider {
      
      Authentication authenticate(Authentication authentication)
              throws AuthenticationException;
    
      boolean supports(Class<?> authentication);
    }
    

    通过第一个方法我们可以拿到form-data的数据,并且返回一个UserDetails如果登录成功的话,或者返回null如果登录失败。

    @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            String userName = (String) authentication.getPrincipal(); //拿到username
            String password = (String) authentication.getCredentials(); //拿到password
           
            UserDetails userDetails = studentService.loadUserByUsername(userName);
            if (/*自定义的验证通过*/) {
                return new UsernamePasswordAuthenticationToken(userDetails, null,userDetails.getAuthorities());
            }
            /*验证不通过*/
            return null;
    

    第二个方法是告诉spring sec我们这个验证支持哪种验证。

    auth.userDetailsService(studentService).passwordEncoder(encoder);
    

    这种验证属于Dao验证。

    还有UsernamePasswordAuthentication验证。
    我们的是UsernamePassword的验证方式,所以在第二个方法中一般会这么写

       @Override
        public boolean supports(Class<?> authentication) {
            return authentication.equals(UsernamePasswordAuthenticationToken.class);
        }
    

    新建类 MyAuthenticationProvider 继承AuthenticationProvider
    完整的代码如下:

    @Component
    public class MyAuthenticationProvider implements AuthenticationProvider {
        /**
         * 注入我们自己定义的用户信息获取对象
         */
        @Autowired
        private MyUserDetailsService userDetailsService;
    
        @Autowired
        private PasswordEncoder passwordEncoder;
    
    
        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            // TODO Auto-generated method stub
            String userName = authentication.getName();// 这个获取表单输入中返回的用户名;
            String password = (String) authentication.getCredentials();// 这个是表单中输入的密码;
            // 这里构建来判断用户是否存在和密码是否正确
            UserDetails userInfo = userDetailsService.loadUserByUsername(userName); // 这里调用我们的自己写的获取用户的方法;
            if (userInfo == null) {
                throw new BadCredentialsException("用户名不存在");
            }
            boolean flag = passwordEncoder.matches(password,userInfo.getPassword());
            if (!flag) {
                throw new BadCredentialsException("密码不正确");
            }
            Collection<? extends GrantedAuthority> authorities = userInfo.getAuthorities();
            // 构建返回的用户登录成功的token
            return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
            //return new UsernamePasswordAuthenticationToken(userInfo, null,authorities);
        }
        @Override
        public boolean supports(Class<?> authentication) {
            // 这里直接改成retrun true;表示是支持这个执行
            return true;
        }
    }
    

    到此为止,我们的用户信息的获取,校验部分已经完成了。接下来要让它起作用,则我们需要在配置文件中修改,让他起作用。回到我的SecurityConfig代码文件,修改如下:

    1、注入我们自己的AuthenticationProvider
    2、修改配置的方法:

        @Autowired
        private AuthenticationProvider provider;  //注入我们自己的AuthenticationProvider
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.authenticationProvider(provider);
        }
    

    现在重新运行程序,则需要输入用户名为 admin 密码是123456之后,才能正常登录了。
    为了方便测试,我们调整添加另一个控制器 /whoim 的代码 ,让他返回当前登录的用户信息,前面说了,他是存在SecurityContextHolder 的全局变量中,所以我们可以这样获取

          @RequestMapping("/whoim")
          @ResponseBody
          public Object whoIm()
          {
                return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
          }
    

    我们运行,直接反问 /whoim ,则直接跳转到登录页面,我们验证过之后,再访问此url,结果如下:


    image.png

    改造3、自定义登录成功和失败的处理逻辑

    在现在的大多数应用中,一般都是前后端分离的,所以我们登录成功或失败都需要用json格式返回,或者登录成功之后,跳转到某个具体的页面。
    接下来我们来实现这种改造。

    为了实现这个功能,只要实现AuthenticationSuccessHandler接口的onAuthenticationSuccess方法即可。
    处理登录成功的:

    @Component
    public class MyAuthenticationSucessHandler implements AuthenticationSuccessHandler {
        private RequestCache requestCache = new HttpSessionRequestCache();
        private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
    
        @Autowired
        private ObjectMapper objectMapper;
    
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                            Authentication authentication) throws IOException, ServletException {
            //response.setContentType("application/json;charset=utf-8");
            //response.getWriter().write(objectMapper.writeValueAsString(authentication));
            SavedRequest savedRequest = requestCache.getRequest(request, response);
            if (savedRequest == null){
                redirectStrategy.sendRedirect(request, response, "/index");
            }else {
                System.out.println(savedRequest.getRedirectUrl());
                redirectStrategy.sendRedirect(request, response, savedRequest.getRedirectUrl());
            }
    
        }
    }
    

    和自定义登录成功处理逻辑类似,自定义登录失败处理逻辑需要实现AuthenticationFailureHandleronAuthenticationFailure方法:

    @Component
    public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    
        @Autowired
        private ObjectMapper mapper;
    
        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                            AuthenticationException exception) throws IOException {
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write(mapper.writeValueAsString(exception.getMessage()));
        }
    }
    

    代码完成之后,修改配置config类代码。
    添加2个注解,自动注入

    @Autowired
          private AuthenticationSuccessHandler myAuthenticationSuccessHandler;
          @Autowired
          private AuthenticationFailureHandler myAuthenticationFailHander;
          
          @Override
          protected void configure(HttpSecurity http) throws Exception {
                // TODO Auto-generated method stub
                //super.configure(http);
                http
                      .formLogin().loginPage("/login").loginProcessingUrl("/login/form")
                      .successHandler(myAuthenticationSuccessHandler)
                      .failureHandler(myAuthenticationFailHander)
                      .permitAll()  //表单登录,permitAll()表示这个不需要验证 登录页面,登录失败页面
                      .and()
                      .authorizeRequests().anyRequest().authenticated()                  
                      .and()
                      .csrf().disable();            
          }
    

    进行测试,成功返回json格式的(登录成功和失败的)

    改造4、添加权限控制

    之前的代码我们用户的权限没有加以利用,现在我们添加权限的用法。
    之前的登录验证通俗的说,就是来判断你是谁(认证),
    而权限控制就是用来确定:你能做什么或者不能做什么(权限)

    在讲这个之前,我们简单说下,对于一些资源不需要权限认证的,那么就可以在Config中添加 过滤条件,如:

    @Override
          protected void configure(HttpSecurity http) throws Exception {
                // TODO Auto-generated method stub
                //super.configure(http);
                http
                      .formLogin().loginPage("/login").loginProcessingUrl("/login/form")
                      .successHandler(myAuthenticationSuccessHandler)
                      .failureHandler(myAuthenticationFailHander)
                      .permitAll()  //表单登录,permitAll()表示这个不需要验证 登录页面,登录失败页面
                      .and()
                      .authorizeRequests()
                            .antMatchers("/index").permitAll()  //这就表示 /index这个页面不需要权限认证,所有人都可以访问
                      .anyRequest().authenticated()  //其他的需要做权限认证            
                      .and()
                      .csrf().disable();            
          }
    

    那么我们直接访问 /index 就不会跳转到登录页面,这样我们就可以把一些不需要验证的资源以这种方式过滤,比如图片,脚本,样式文件之类的。

    我们先来看第一种权限控制:在编码中写死的。
    其实权限控制也是通过这种方式来实现:

       http
                      .formLogin().loginPage("/login").loginProcessingUrl("/login/form")
                      .successHandler(myAuthenticationSuccessHandler)
                      .failureHandler(myAuthenticationFailHander)
                      .permitAll()  //表单登录,permitAll()表示这个不需要验证 登录页面,登录失败页面
                      .and()
                      .authorizeRequests()
                            .antMatchers("/index").permitAll()                    
                      .antMatchers("/whoim").hasRole("ADMIN") //这就表示/whoim的这个资源需要有ROLE_ADMIN的这个角色才能访问。不然就会提示拒绝访问
                      .anyRequest().authenticated() //必须经过认证以后才能访问          
                      .and()
                      .csrf().disable();   
    

    这个用户的角色哪里来,就是我们自己的UserDetailsService中返回的用户信息中的角色权限信息,
    这里需要注意一下就是 .hasRole("ADMIN"),那么给用户的角色时就要用:ROLE_ADMIN

    .antMatchers 这里也可以限定HttpMethod的不同要求不同的权限(适用于Restful风格的API).
    如:Post需要 管理员权限,get 需要user权限,我们可以这么个改造,同时也可以通过通配符来是实现 如:/user/1 这种带参数的URL

    .antMatchers("/whoim").hasRole("ADMIN")
    .antMatchers(HttpMethod.POST,"/user/").hasRole("ADMIN")
    .antMatchers(HttpMethod.GET,"/user/
    ").hasRole("USER")

    Spring Security 的校验的原理:左手配置信息,右手登录后的用户信息,中间投票器。
    从我们的配置信息中获取相关的URL和需要的权限信息,然后获得登录后的用户信息,
    然后经过:AccessDecisionManager 来验证,这里面有多个投票器:AccessDecisionVoter,(默认有几种实现:比如:1票否决(只要有一个不同意,就没有权限),全票通过,才算通过;只要有1个通过,就全部通过。类似这种的。
    WebExpressionVoter 是Spring Security默认提供的的web开发的投票器。(表达式的投票器)

    Spring Security 默认的是 AffirmativeBased 只要有一个通过,就通过。
    有兴趣的可以 从FilterSecurityInterceptor这个过滤器入口,来查看这个流程。
    内嵌的表达式有:permitAll denyAll 等等。
    每一个权限表达式都对应一个方法。
    如果需要同时满足多个要求的,不能连写如 ,我们有个URL需要管理员权限也同时要限定IP的话,不能:.hasRole("ADMIN").hasIPAddress("192.168.1.1");
    而是需要用access方法 .access("hasRole('ADMIN') and hasIpAddress('192.168.1.1')");这种。

    那我们可以自己写权限表达式吗? 可以,稍后。。。这些都是硬编码的实现,都是在代码中写入的,这样的灵活性不够。所以我们接下来继续改造

    改造4、添加基于RBAC(role-Based-access control)权限控制

    这个大家可以去百度一下,一般都是由 3个部分组成,一个是用户,一个是角色 ,一个是资源(菜单,按钮),然后就是 用户和角色的关联表,角色和资源的关联表

    核心就是判断当前的用户所拥有的URL是否和当前访问的URL是否匹配。

    首先我们自己提供一个判断的接口和实现,代码如下:

    /**
     * 返回权限验证的接口
     */
    public interface RbacService {
          boolean hasPermission(HttpServletRequest request,Authentication authentication);
    }
    
    @Component("rbacService")
    public class RbacServiceImpl implements RbacService {
          private AntPathMatcher antPathMatcher = new AntPathMatcher();
          @Override
          public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
                Object principal = authentication.getPrincipal();
                boolean hasPermission = false;
                if (principal instanceof UserDetails) { //首先判断先当前用户是否是我们UserDetails对象。
                      String userName = ((UserDetails) principal).getUsername();
                      Set<String> urls = new HashSet<>(); // 数据库读取 //读取用户所拥有权限的所有URL
                      
                      urls.add("/whoim");
                      // 注意这里不能用equal来判断,因为有些URL是有参数的,所以要用AntPathMatcher来比较
                      for (String url : urls) {
                            if (antPathMatcher.match(url, request.getRequestURI())) {
                                  hasPermission = true;
                                  break;
                            }
                      }
                }
                return hasPermission;
          }
    }
    

    然后在Security的配置项中添加自定义的权限表达式就可以了。

    @Override
          protected void configure(HttpSecurity http) throws Exception {
                // TODO Auto-generated method stub
                //super.configure(http);
                http
                      .formLogin().loginPage("/login").loginProcessingUrl("/login/form")
                      .successHandler(myAuthenticationSuccessHandler)
                      .failureHandler(myAuthenticationFailHander)
                      .permitAll()  //表单登录,permitAll()表示这个不需要验证 登录页面,登录失败页面
                      .and()
                      .authorizeRequests()
    //                      .antMatchers("/index").permitAll()                    
    //                .antMatchers("/whoim").hasRole("ADMIN")
    //                .antMatchers(HttpMethod.POST,"/user/*").hasRole("ADMIN")
    //                .antMatchers(HttpMethod.GET,"/user/*").hasRole("USER")
                      .anyRequest().access("@rbacService.hasPermission(request,authentication)")    //必须经过认证以后才能访问            
                      .and()
                      .csrf().disable();            
          }
    

    其中 @rbacService 就是我们自己声明的bean,在RbacServiceImpl实现类的头部注解中。

    改造5、记住我的功能Remeber me

    本质是通过token来读取用户信息,所以服务端需要存储下token信息
    根据官方的文档,token可以通过数据库存储 数据库脚本

    CREATE TABLE persistent_logins (
        username VARCHAR(64) NOT NULL,
        series VARCHAR(64) NOT NULL,
        token VARCHAR(64) NOT NULL,
        last_used TIMESTAMP NOT NULL,
        PRIMARY KEY (series)
    );
    

    然后,配置好token 的存储 及数据源

     @Autowired
          private DataSource dataSource;   //是在application.properites
    
          /**
           * 记住我功能的token存取器配置
           * @return
           */
          @Bean
          public PersistentTokenRepository persistentTokenRepository() {
                JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
                tokenRepository.setDataSource(dataSource);
                return tokenRepository;
          }
    

    修改Security配置

      @Override
          protected void configure(HttpSecurity http) throws Exception {
                // TODO Auto-generated method stub
                //super.configure(http);
                http
                      .formLogin().loginPage("/login").loginProcessingUrl("/login/form")
                      .successHandler(myAuthenticationSuccessHandler)
                      .failureHandler(myAuthenticationFailHander)
                      .permitAll()  //表单登录,permitAll()表示这个不需要验证 登录页面,登录失败页面
                      .and()
                      .rememberMe()
                            .rememberMeParameter("remember-me").userDetailsService(userDetailsService)
                            .tokenRepository(persistentTokenRepository())
                            .tokenValiditySeconds(60)
                      .and()
                      .authorizeRequests()
    //                      .antMatchers("/index").permitAll()                    
    //                .antMatchers("/whoim").hasRole("ADMIN")
    //                .antMatchers(HttpMethod.POST,"/user/*").hasRole("ADMIN")
    //                .antMatchers(HttpMethod.GET,"/user/*").hasRole("USER")
                      .anyRequest().access("@rbacService.hasPermission(request,authentication)")    //必须经过认证以后才能访问            
                      .and()
                      .csrf().disable();      
    

    在登陆页面login.html上还要加上记住密码的勾选框

     <tr>
                <td colspan="2"><input type="checkbox" name="remember-me" value="true"/>记住我</td>
            </tr>
    

    登录之后 数据库就会有一条数据


    image.png

    然后,服务重新启动下,我们在看下直接访问 /whoim 的话,就可以直接访问了,不需要再登录了。

    到此为止我们的Spring Securtiy 的基本用法已经改造完成了。

    源码链接:https://github.com/visionsws/vicente-demo


    参考文章
    Spring boot 中 Spring Security 使用改造5部曲
    SpringBoot + Spring Security 基本使用及个性化登录配置
    Hello Spring Security with Boot

    相关文章

      网友评论

        本文标题:SpringBoot中使用Spring Security

        本文链接:https://www.haomeiwen.com/subject/efsflqtx.html