Spring Security集成Oauth2实现用户身份验证+授权

论坛 期权论坛 脚本     
已经匿名di用户   2022-2-7 16:34   2415   0

前言

最近的项目有使用到Sping Security和Oauth做用户身份验证和授权机制,所以在网上找了点资料简单学习并写了个小demo,在此进行记录总结。授权这块是直接拷贝了参考链接中的小demo,后续涉及到第三方应用授权码方式授权的话再学习然后总结

参考链接

1. Spring Security了解:https://www.springcloud.cc/spring-security-zhcn.html

2. Spring Boot集成Spring Security:

https://www.jianshu.com/p/afe6619d9663

https://www.cnblogs.com/lenve/p/11242055.html

https://blog.csdn.net/qq_29580525/article/details/79317969

https://blog.csdn.net/yuanlaijike/article/details/80249235

https://blog.csdn.net/yuanlaijike/article/details/84703690

3. Oauth2了解:

http://www.ruanyifeng.com/blog/2019/04/oauth_design.html

http://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html

https://www.cnblogs.com/meibaorui/p/9182660.html

4. Spring Security集成Oauth2:

https://www.cnblogs.com/fernfei/p/12200226.html

https://blog.csdn.net/weixin_34080571/article/details/91715402

https://www.cnblogs.com/dalianpai/p/12425962.html

实现过程

1. Spring Boot集成Spring Security

1.1 导入依赖以及配置服务器相关属性

1.1.1 导入依赖

可直接建立Spring Boot项目选择Spring Security,或者也可以建立一个空的Spring Boot项目后在pom.xml依赖配置文件中加入相关依赖。

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

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

1.1.2 配置服务器相关属性

一般配置服务器的访问端口和初始路径,默认为8080以及/,配置在resources/application.properties中配置

#配置服务器端口号
server.port=80
#配置服务器入口网址
server.servlet.context-path=/spring

1.2 普通web访问实现(UserController)

这里是写了个登录、home页面,但是由于没有写相应的前端页面,所以当用户跳转/login页面时会显示404错误,以及用户验证成功后跳转的home页面也会显示404错误。此处说明一下实际中会根据需要在SecurityConfig中对页面是否拦截以及跳转情况进行统一规范设计,此demo中直接使用Spring Security默认的login表单,用户身份信息验证成功后会将信息直接打印输出到前端。

@RestController()
@RequestMapping("/user")
public class UserController {

    @PostMapping("login")
    public String login(){
        return "login.html";
    }

    //由于没有写对应的静态页面,所以用户身份验证成功后访问这些页面会报404错误;
    // 如果身份未验证成功会跳转login页面(自己手动写的/Spring Security默认的login表单)
    @GetMapping("home")
    public String home(){
        return "home.html";
    }

    /**
     * 返回当前登录的用户信息(存在SecurityContextHolder的全局变量中)
     * @return
     */
    @GetMapping("/whoim")
    //@PreAuthorize("hasRole('ROLE_USER')") //@PreAuthorize 用于判断用户是否有指定权限,没有就不能访问
    public Object whoIm(){
        return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    }

1.3 集成Spring Security(SecurityConfig)使用Java内存用户名和密码(指定可登录/通过拦截的用户名和密码,其中密码为明文)

SecurityConfig类继承WebSecurityConfigurerAdapter类,主要实现几个config方法,用来进行接口请求拦截/用户身份验证/前端静态页面拦截

/**
 * 实现一个WebSecurityConfigurerAdapter然后重写一下configure方法,配置登录验证页、请求权限设置
 * 标识该类是配置类、开启 Security 服务、开启全局 Securtiy 注解
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) //启用基于注解的安全性 prePostEnable表示使用基于表达书的语法
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    ObjectMapper objectMapper; //json转换

    /**
     * 接口请求拦截
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests() //安全请求策略,方法可有多个子节点matcher,每个matcher按照其声明顺序执行
                //页面访问权限控制说明
                .antMatchers("/").permitAll() //允许任何用户访问/路径(这个路径得有页面,否则后报404错误)
                .antMatchers("/admin/**").hasRole("ADMIN") //以/admin/开头的URL只能由拥有ROLE_ADMIN角色的用户访问
                .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") //同理以/db/开头的URL
                // 只能由同时具有ROLE_ADMIN和ROLE_DBA的用户访问
                .anyRequest()
//                .access("权限认证bean") 必须经过权限认证以后才能访问
                .authenticated() //其它请求进行拦截(需要进行认证)
                .and()
            .formLogin() //支持基于表单的登录验证
//                .loginPage("../../resources/static/login") //指定登录的路径(用户需要登录验证时跳转login页面)
//                .loginPage("/user/login") //指定跳转controller层的相关方法中
//                .loginProcessingUrl("/index")  //登录成功后跳转/index页面(拦截通过后)
                .permitAll() //允许基于表单登录的所有的URL的所有用户的访问
                .successHandler(new UserAuthenticationSuccessHandler())
                .failureHandler(new UserAuthenticationFailureHandler())
//                .and() //and,相当于XML标签的关闭,允许继续配置父类节点
//            .rememberMe() //实现rememberMe功能(短时间内免登录)
//                .rememberMeParameter("remember me").userDetailsService(userDetailsService) //remember me为参数
//                .tokenRepository(persistenceTokenRepository) //定义好的token存储类
//                .tokenValiditySeconds(60) //token有效时间为60s
                .and()
            .logout() //提供注销支持
                .logoutUrl("/logout") //注销时触发跳转/logout页面
                .logoutSuccessUrl("/index") //注销成功后跳转/index页面
                .logoutSuccessHandler(new LogoutSuccessHandler(){ //匿名内部类
                    @Override
                    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=UTF-8");
                        httpServletResponse.getWriter().println("登出成功");
                    }
                }) //指定后/index会被忽略,注销完成执行logoutSuccessHandler,可抛出异常
                .invalidateHttpSession(true) //注销时让HttpSession无效
//                .addLogoutHandler(logoutHandler) //被用来执行必要的清理,不可抛出异常
//                .deleteCookies(cookieNamesToClear) //注销成功后移除命名为cookieNameToClear的cookie
                .permitAll() //允许任何用户访问
                .and()
//            .httpBasic() //允许用户使用基于HTTP验证进行认证 (Spring Security支持两种认证方式:HTTP和Form表单)
//                .and()
            .csrf().disable(); //禁用CSRF保护
        http.cors().disable();
    }

    /**
     * 设置通过请求拦截(登录验证)放行后/登录成功后的处理
     * 此处也可直接写在configure(HttpSecurity http)当做匿名内部类写法/Lambda表达式实现
     */
    @Component("userAuthenticationSuccessHandler")
    public class UserAuthenticationSuccessHandler implements AuthenticationSuccessHandler{
        private Logger logger = LoggerFactory.getLogger(UserAuthenticationSuccessHandler.class);

        @Override
        public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
            logger.info("登录成功");
            //设置返回Response的类型和值
            httpServletResponse.setContentType("application/json;charset=UTF-8");
            //转为json格式返回到前端
            httpServletResponse.getWriter().write(objectMapper.writeValueAsString(authentication));
            System.out.println(SecurityContextHolder.getContext().getAuthentication().getPrincipal());

            //跳转whoim页面
//            new DefaultRedirectStrategy().sendRedirect(httpServletRequest,httpServletResponse,"/whoim");
        }
    }

    /**
     * 设置通过请求拦截(登录验证)拦截成功后/登录失败后的处理
     */
    @Component("userAuthenticationFailureHandler")
    public class UserAuthenticationFailureHandler implements AuthenticationFailureHandler{
        private Logger logger = LoggerFactory.getLogger(UserAuthenticationFailureHandler.class);

        @Override
        public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
            logger.info("登录失败");
            httpServletResponse.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            httpServletResponse.setContentType("application/json;charset=UTF-8");
            httpServletResponse.getWriter().write(objectMapper.writeValueAsString(e));
        }
    }

    /**
     * 配置一个正常的验证
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //配置两个用户+密码+角色可验证成功(Java内存配置用户名和密码)
        auth.inMemoryAuthentication().passwordEncoder(NoOpPasswordEncoder.getInstance()) //使用明文,不对密码进行加密操作
                //NoOpPasswordEncoder是一个final类,使用getInstance静态方法获取实例(该类中对密码不做任何加密处理)
                .withUser("user").password("123456").roles("USER")
                .and()
                .withUser("admin").password("admin").roles("USER","ADMIN");
    }

    /**
     * 前端(静态页面)拦截
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/js/**","/css/**","/images/**"); //对静态js、css、images放行
    }
}

NoOpPasswordEncoder类如下图:

1.4 改造实现密码加密(AuthProvider、UserDetailServiceImpl)

实际应用中明文密码太过于不安全,所以一般会定义加密对象,对密码进行加密操作。同时实际中用户名和密码基本都是有数据库支撑的,所以基本逻辑是拦截后进行根据用户输入的用户名和密码 然后拿着用户名去数据库中找,如果找到了用户,那么进行加密后的密码匹配操作,如果匹配成功那么说明用户身份认证成功,进行成功后的相关处理即可(一般会存储session保证用户短时间内的免登录操作,另外会将用户权限加入security权限列表,如果系统支持RABC那么还会与角色相关联,实现基于角色的用户权限管理操作)

1.4.1 SecurityConfig

主要改动的地方在于配置用户身份验证的configure方法

    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //一般是数据库中获取用户名和密码进行匹配
      auth.authenticationProvider(authProvider).userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
/**
 * 实现一个WebSecurityConfigurerAdapter然后重写一下configure方法,配置登录验证页、请求权限设置
 * 标识该类是配置类、开启 Security 服务、开启全局 Securtiy 注解
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) //启用基于注解的安全性 prePostEnable表示使用基于表达书的语法
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    ObjectMapper objectMapper; //json转换
    @Autowired
    AuthProvider authProvider;
    @Autowired
    UserDetailsServiceImpl userDetailsService;

    /**
     * springboot2.x引入的security版本是5.x的,这个版本需要提供一个PasswordEncoder实例,不然就会报错
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 身份认证管理
     * @return
     * @throws Exception
     */
    @Override
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    /**
     * 接口请求拦截
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests() //安全请求策略,方法可有多个子节点matcher,每个matcher按照其声明顺序执行
                //页面访问权限控制说明
                .antMatchers("/").permitAll() //允许任何用户访问/路径(这个路径得有页面,否则后报404错误)
                .antMatchers("/admin/**").hasRole("ADMIN") //以/admin/开头的URL只能由拥有ROLE_ADMIN角色的用户访问
                .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") //同理以/db/开头的URL
                // 只能由同时具有ROLE_ADMIN和ROLE_DBA的用户访问
                .anyRequest()
                .authenticated() //其它请求进行拦截(需要进行认证)
                .and()
            .formLogin() //支持基于表单的登录验证
                .successHandler(new UserAuthenticationSuccessHandler())
                .failureHandler(new UserAuthenticationFailureHandler())
                .permitAll() //允许基于表单登录的所有的URL的所有用户的访问
                .and()
            .logout() //提供注销支持
                .logoutUrl("/logout") //注销时触发跳转/logout页面
                .logoutSuccessUrl("/index") //注销成功后跳转/index页面
                .logoutSuccessHandler(new LogoutSuccessHandler(){ //匿名内部类
                    @Override
                    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=UTF-8");
                        httpServletResponse.getWriter().println("登出成功");
                    }
                }) //指定后/index会被忽略,注销完成执行logoutSuccessHandler,可抛出异常
                .invalidateHttpSession(true) //注销时让HttpSession无效
                .permitAll() //允许任何用户访问
                .and()
            .csrf().disable(); //禁用CSRF保护
        http.cors().disable();
    }

    /**
     * 设置通过请求拦截(登录验证)放行后/登录成功后的处理
     * 此处也可直接写在configure(HttpSecurity http)当做匿名内部类写法/Lambda表达式实现
     */
    @Component("userAuthenticationSuccessHandler")
    public class UserAuthenticationSuccessHandler implements AuthenticationSuccessHandler{
        private Logger logger = LoggerFactory.getLogger(UserAuthenticationSuccessHandler.class);

        @Override
        public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
            logger.info("登录成功");
            //设置返回Response的类型和值
            httpServletResponse.setContentType("application/json;charset=UTF-8");
            //转为json格式返回到前端
            httpServletResponse.getWriter().write(objectMapper.writeValueAsString(authentication));
            System.out.println(SecurityContextHolder.getContext().getAuthentication().getPrincipal());
            //如果需要保存session则在此处进行对request进行session保存操作(如果有多个用户并且跨域,可考虑将session存储在redis缓存数据库中)
            //跳转whoim页面
//            new DefaultRedirectStrategy().sendRedirect(httpServletRequest,httpServletResponse,"/whoim");
        }
    }

    /**
     * 设置通过请求拦截(登录验证)拦截成功后/登录失败后的处理
     */
    @Component("userAuthenticationFailureHandler")
    public class UserAuthenticationFailureHandler implements AuthenticationFailureHandler{
        private Logger logger = LoggerFactory.getLogger(UserAuthenticationFailureHandler.class);

        @Override
        public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
            logger.info("登录失败");
            httpServletResponse.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            httpServletResponse.setContentType("application/json;charset=UTF-8");
            httpServletResponse.getWriter().write(objectMapper.writeValueAsString(e));
        }
    }

    /**
     * 配置一个正常的验证
     * 将我们自定义的 userDetailsService 注入进来,
     * 在 configure()方法中使用 auth.userDetailsService()方法替换掉默认的 userDetailsService
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //一般是数据库中获取用户名和密码进行匹配
        auth.authenticationProvider(authProvider).userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    /**
     * 前端(静态页面)拦截
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/js/**","/css/**","/images/**"); //对静态js、css、images放行
    }
}

1.4.2 UserDetailsServiceImpl

该类主要是注入用户的信息(用户名和密码)返回一个User对象

/**
 * 实现UserDetailsService接口进行用户姓名密码校验(将用户信息和权限注入进来)
 * 返回值是UserDetails,是一个接口,
 * 一般使用其子类org.springframework.security.core.userdetails.User,它有三个参数,分别是用户名、密码和权限集
 */
@Component
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    PasswordEncoder passwordEncoder;

    //创建一个日志对象
    private Logger logger = LoggerFactory.getLogger(UserDetailsService.class);

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        logger.info("登录用户输入用户名:{}",username);
        //根据username查找用户信息
        //...此处应该为调用数据库mapper类的根据用户名查找用户操作

        //密码进行加密
        String pwd = "123456"; //实际应该根据数据库进行加密
        //对密码进行brypt加密
        String cyptPwd = passwordEncoder.encode(pwd);
        //一般如果有数据库的话密码加密操作在添加用户的时候已经实现,这里只需要获取到用户的密码即可

        logger.info("加密后密码为:{}",cyptPwd);
        //返回账号 密码 权限
        return new User("username",cyptPwd, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
    }
}

1.4.3 AuthProvider

该类实现用户名和密码的匹配验证操作

@Component //勿忘
public class AuthProvider implements AuthenticationProvider {
    @Autowired
    UserDetailsServiceImpl userDetailsService;
    @Autowired
    PasswordEncoder passwordEncoder;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        //获取用户名和密码
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();
        //进行密码比对(解密后的)
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        boolean flag = passwordEncoder.matches(password,userDetails.getPassword());
        if(flag){//验证通过
            return new UsernamePasswordAuthenticationToken(username,password,userDetails.getAuthorities()); //返回一个token对象,参数为用户名、密码和权限列表
        }
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

1.5 测试

1.5.1 未登录输入网址会跳转login页面

输入网址自动跳转login页面

1.5.2 登录后可正常访问系统的页面(由于没写前端html,所以404)登录成功根据successHandler将提示信息返回到浏览器页面上。

1.6 注解问题

对于SpringBoot项目中的配置类都需要加@Configuration注解进行注册,否则会报错无效

生成的实体方法/类需要加@Bean注解进行注册

SecurityConfig方法中的注解表示该类是配置类、开启 Security 服务、开启全局 Securtiy 注解

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) //启用基于注解的安全性 prePostEnable表示使用基于表达书的语法

1.7 Spring Security简介

一个Web应用程序会有很多的潜在漏洞,例如跨站脚本、请求伪造和会话劫持。Spring Security是一个强大高可定制的身份验证和访问控制框架,是确保基于Spring的应用程序的标准,可尽可能的避免这些潜在漏洞。其底层实现为一条过滤器链,就是用户请求进来,判断有没有请求的权限,抛出异常,重定向跳转

认证/身份验证:为用户建立一个他所声明的主体,主体一般是指用户、设备或者可以在系统中执行动作的其他系统

授权/访问控制:一个用户能够在应用中执行某个操作,在到达授权判断之前,身份的主体已由身份验证过程建立。Spring Security提供的授权功能:授权web请求、授权方法是否可被调用、授权访问单个域对象的实例(先认证再授权)

核心组件:spring-security-core

SecurityContextHolder:提供几种访问SecurityContext的方式。存储当前应用程序的安全环境,包含应用当前使用的主体细节,默认使用ThreadLocal存储(安全环境在同一个线程执行的方法一直有效),可通过设置系统属性,或者调用SecurityContextHolder的静态方法更改默认的SecurityContextHolder.MODE_THREADLCOAL模式。

SecurityContext:保存Authentication信息和请求对应的安全信息

Authentication:存储目前与应用程序交互的主要细节

GrantedAuthority:在应用程序范围赋予主体的权限

UserDetails:Spring Security的核心接口,是从Authentication中获得的安全主体

UserDetailsService:创建一个UserDetails,传递一个String类型的用户名/证书ID(根据SecurityConfig实现一个大的验证拦截框框/过滤器,然后通过AuthProvider进行身份验证,其中匹配的对象是通过UserDetailsService返回的一个UserDetails目标用户验证对象)

2. Spring Security集成Oauth2

2.1 导入依赖

       <dependency>
   <groupId>org.springframework.security.oauth</groupId>
   <artifactId>spring-security-oauth2</artifactId>
  </dependency>

2.2 Spring Security集成Oauth2

2.2.1 授权服务器配置(AuthorizationServiceConfig)

@Configuration //配置类
@EnableAuthorizationServer //开启授权服务
public class AuthorizationServiceConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    AuthenticationManager authenticationManager;
    @Autowired
    RedisConnectionFactory redisConnectionFactory; //redis连接池
    @Autowired
    UserDetailsServiceImpl userDetailsService;

    /**
     * // 定义了客户端应用的通行证
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("client_id") //客户端账号
                .authorizedGrantTypes("password","refresh_token") //授权类型(支持四种类型:授权码类型、隐藏式类型、密码类型、客户端凭证,以密码类型为例)
                .accessTokenValiditySeconds(60*60*24) //授权有效期1天
                .refreshTokenValiditySeconds(60*60*1) //刷新有效期1h
                .resourceIds("rid")
                .scopes("all") //授权作用域
//                .secret("secretKey") //密码
                //对密码加密
                .secret(new BCryptPasswordEncoder().encode("secretKey"))
                .redirectUris("http://localhost:80/spring/login"); //验证回调地址
    }

    /**
     * 身份验证管理
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //不同的验证方式此处写法不同
        //使用密码验证方式授权
        endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory)) //token存储在redis中
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService);
    }

    /**
     * 验证方式
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //允许用户表单身份验证
        security.allowFormAuthenticationForClients();
    }
}

2.2.2 资源服务器配置(ResourceServerConfig)

一般来讲授权服务器和资源服务器会分开,这个demo是整合在一台机子上配置。

@Configuration
@EnableResourceServer //开启资源服务
public class ResourceServiceConfig extends ResourceServerConfigurerAdapter {

    /**
     * 资源访问授权配置(哪些资源访问需要进行授权)
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasRole("USER")
                .anyRequest().authenticated();//其余所有资源访问都需要进行授权
    }

    /**
     * 可访问的资源
     * @param resources
     * @throws Exception
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId("rid");
    }
}

2.3.3 修改Spring Security配置(SecurityConfig)

添加oauth链接的拦截

http.requestMatchers()
                .antMatchers("/oauth/**","/spring/login/**","/logout/**") //放行oauth授权页面,login页面,logout页面
                .and()
                .authorizeRequests() //安全请求策略,方法可有多个子节点matcher,每个matcher按照其声明顺序执行
                .antMatchers("/oauth/**").authenticated()
                .and()
            .formLogin() //支持基于表单的登录验证
                .permitAll() //允许基于表单登录的所有的URL的所有用户的访问
                .successHandler(new UserAuthenticationSuccessHandler())
                .failureHandler(new UserAuthenticationFailureHandler())
                .and()
            .logout() //提供注销支持
                .logoutUrl("/logout") //注销时触发跳转/logout页面
                .logoutSuccessUrl("/index") //注销成功后跳转/index页面
                .logoutSuccessHandler(new LogoutSuccessHandler(){ //匿名内部类
                    @Override
                    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=UTF-8");
                        httpServletResponse.getWriter().println("登出成功");
                    }
                }) //指定后/index会被忽略,注销完成执行logoutSuccessHandler,可抛出异常
                .invalidateHttpSession(true) //注销时让HttpSession无效
                .permitAll() //允许任何用户访问
                .and()
            .csrf().disable(); //禁用CSRF保护

2.4 Oauth2简介

OAuth(Open Authorization开放授权)是一个开放标准,允许用户授权第三方移动应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容。

授权模式有四种:授权码模式、隐藏模式、密码模式、客户端模式。其中一般第三方应用都使用的是授权码模式。

给一个以QQ为实例的授权码权限验证模式的链接:https://blog.csdn.net/weixin_43553694/article/details/105223236

简单来说就是用户先向授权服务器申请授权码(申请的过程需要持有资源的用户手动同意),申请成功后拿着这个授权码再去服务器申请一个令牌(token);拿到token以后发送token到资源服务器中访问资源,资源服务器拿到这个token进行验证,验证令牌成功则允许用户访问资源,否则拒绝用户访问。

总结

学习的两个路径,其一是沿着学习路线看视频看教程写demo进行总结;其二是根据实际项目中遇到的问题和技术点进行研究学习写demo进行总结。学就完事儿了。

另外这篇博客有些过于基础了,之后深入学习后继续进行不断总结。

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:81
帖子:4969
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP