Spring Security 和Swagger常用注解

Spring Security 和Swagger常用注解

一、Spring Security概述

Spring Security 是一个能够为基于 Spring 的企业应用系统提供声明式的安全访问控制解决方案的安全框架

Spring Security的核心:认证(登录),鉴权

1、security认证入门案例

  • 添加依赖

    <dependencies>
            <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-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
  • 启动项目

    security默认提供了一个登录界面,访问地址为:http://127.0.0.1:8080/login

    默认的帐号:user    
    默认密码:会在控制台打印
    	Using generated security password: e6b75106-50f0-446c-8d62-ee7ab14b27dd
    

    security默认提供了退出系统的业务,访问地址为:http://127.0.0.1:8080/logout

2、使用自定义帐号密码实现认证

在security中用户的密码必须是加密的。否则报错。

Spring Security 内置的 Password Encoder 有:

加密算法名称PasswordEncoder
NOOPNoOpPasswordEncoder.getInstance()
SHA256new StandardPasswordEncoder()
BCRYPT(官方推荐)new BCryptPasswordEncoder()
LDAPnew LdapShaPasswordEncoder()
PBKDF2new Pbkdf2PasswordEncoder()
SCRYPTnew SCryptPasswordEncoder()
MD4new Md4PasswordEncoder()
MD5new MessageDigestPasswordEncoder(“MD5”)
SHA_1new MessageDigestPasswordEncoder(“SHA-1”)
SHA_256new MessageDigestPasswordEncoder(“SHA-256”)
/**
 * spring security配置类(重点)
 */
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 实例化密码管理器
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 该方法是认证的起点
     *   作用:  获得登录页面用户提交的帐号和密码
     *          将用户输入的帐号和密码与正确的帐号和密码做对比
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
       String pwd= passwordEncoder().encode("123");
        auth.inMemoryAuthentication().withUser("tom")  //正确的帐号
                .password(pwd) //用户正确的密码
                .roles("admin");  //用户拥有的权限编码
    }
}

二、根据数据库实现认证

1、认证实现

security中的认证请求只能以post方式发送queryString

  • 添加依赖

     <dependencies>
            <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>
    
    
            <!--mysql驱动-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
    
            <!--  springboot 整合mybaits plus 相关  -->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.4.2</version>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-generator</artifactId>
                <version>3.4.0</version>
                <scope>compile</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.velocity</groupId>
                <artifactId>velocity-engine-core</artifactId>
                <version>2.2</version>
            </dependency>
    
            <!--实体类工具-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
  • 根据用户输入的帐号获得正确的用户信息(用户信息,用户的所有角色编码,用户所有的权限编码)

  • 创建处理登录业务的service

    @Service
    @Transactional
    @Slf4j
    public class LoginService implements UserDetailsService {
    
        @Resource
        private UserinfoMapper userinfoMapper;
    
        @Resource
        private RoleMapper roleMapper;
    
        @Resource
        private PermissionMapper permissionMapper;
    
        /**
         * UserDetailsService对象是security中用来提供用户正确的帐号,密码,权限的对象
         * security中接收登录页面发过来的帐号和密码默认:username,password
         * @param username the username identifying the user whose data is required.
         *
         * @return
         * @throws UsernameNotFoundException
         */
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            //根据帐号查询用户信息
            Userinfo userinfo=userinfoMapper.getUserinfoByAccount(username);
    
            String authorities="";//保存用户拥有的权限
            if(userinfo!=null){
                //根据用户id查询到用户拥有的角色+权限编码
                List<String> roleCode=roleMapper.listRoleCode(userinfo.getId());
                List<String> perCode=permissionMapper.listPermissionCode(userinfo.getId());
    
                authorities= StringUtils.join(roleCode,",");
               authorities=authorities+","+StringUtils.join(perCode,",");
    
                log.info("用户权限:{}",authorities);
            }
    
            //将查询出来的用户信息,转为security的UserDetails对象,
            //参数一:正确的帐号   参数二:正确的密码    参数三:用户拥有的权限(角色+权限编码)
            return new User(userinfo.getAccount(),userinfo.getPassword() ,
                    AuthorityUtils.commaSeparatedStringToAuthorityList(authorities));
        }
    }
    
  • 修改security配置类

    /**
     * spring security配置类(重点)
     */
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        //认证业务类
        @Resource
        private LoginService loginService;
    
        /**
         * 实例化密码管理器
         * @return
         */
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        /**
         * 该方法是认证的起点
         *   作用:  获得登录页面用户提交的帐号和密码
         *          将用户输入的帐号和密码与正确的帐号和密码做对比
         * @param auth
         * @throws Exception
         */
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(loginService).passwordEncoder(passwordEncoder());
        }
    }
    

2、security内置过滤器【重点】

security默认有13个过滤器,不同的功能由不同的过滤器来执行,我们只需要记住下面四个即可

  • LogoutFilter: 在过滤器链的第三个,用来拦截用户的退出请求。默认删除保存在security的session中存的认证令牌

  • UsernamePasswordAuthenticationFilter:认证的过滤器,当用户发送logitn请求时会被该过滤器拦截(获得用户输入的帐号和密码)

    security中的认证请求必须是post方式且数据格式为queryString

  • ExceptionTranslationFilter: 异常过滤器,security中所有的异常都由该过滤器捕获,在过滤器链的倒数第二个

  • FilterSecurityInterceptor: 鉴权过滤器,在过滤器链的倒数第一个

注意:security中出现的异常,只能由security自己才能捕获到。

3、认证失败后的处理(返回json提示)

  • 修改security配置类

    /**
     * spring security配置类(重点)
     */
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Resource
        private LoginService loginService;
    
        /**
         * 实例化密码管理器
         * @return
         */
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        /**
         * 该方法是认证的起点
         *   作用:  获得登录页面用户提交的帐号和密码
         *          将用户输入的帐号和密码与正确的帐号和密码做对比
         * @param auth
         * @throws Exception
         */
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(loginService).passwordEncoder(passwordEncoder());
        }
    
    
        /**
         * security过滤器链操作
         * @param http
         * @throws Exception
         */
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            //鉴权相关的过滤器  暂时不管
            http.authorizeRequests()
                    .anyRequest()
                    .authenticated();   // 1
    
            //配置与登录相关的过滤器
            http.formLogin()
                    .permitAll();//这句配置很重要,新手容易忘记。
    
            //关闭security  session(使用redis存用户的认证token)
            http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    
            http.csrf().disable();  //关闭csrf
        }
    }
    

    认证过滤器配置补充说明【了解】:

    //配置与登录相关的过滤器
            http.formLogin()
                    .loginPage("/index.html")  //设置项目默认登录页面
                    .loginProcessingUrl("/dologin") //修改认证请求的url
                    .usernameParameter("account") //用户的帐号必须叫account
                    .passwordParameter("pwd")
                    .permitAll();//这句配置很重要,新手容易忘记。放开 login.html和dologin 的访问权
    
  • 创建认证失败的处理器

    /**
     * 认证失败的处理器
     */
    @Component
    public class SimpleAuthenticationFailureHandler implements AuthenticationFailureHandler {
    
        @Override
        public void onAuthenticationFailure(HttpServletRequest httpServletRequest,
                                            HttpServletResponse httpServletResponse,
                                            AuthenticationException exception) throws IOException, ServletException {
            //设置响应数据的编码
            httpServletResponse.setContentType("application/json;charset=UTF-8");
    
            ResponseResult<Void> responseResult=null;
    
            if(exception instanceof InternalAuthenticationServiceException){
                responseResult=new ResponseResult<Void>(2007, "帐号不存在");
            }
            if(exception instanceof BadCredentialsException){
                responseResult=new ResponseResult<Void>(2003, "密码错误");
            }
    
            //输出json字符串到客户端
            PrintWriter printWriter =httpServletResponse.getWriter();
            printWriter.print(JacksonUtil.toJsonString(responseResult));
            printWriter.flush();
            printWriter.close();
        }
    }
    
    
  • 将认证失败处理器注册到认证过滤器中

     /**
         * security过滤器链操作
         * @param http
         * @throws Exception
         */
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            //鉴权相关的过滤器  暂时不管
            http.authorizeRequests()
                    .anyRequest()
                    .authenticated();   // 1
    
            //配置与登录相关的过滤器
            http.formLogin()
                    .failureHandler(simpleAuthenticationFailureHandler)  //认证失败后的处理器
                    .permitAll();//这句配置很重要,新手容易忘记。放开 login.html和dologin 的访问权
    
            //关闭security  session(使用redis存用户的认证token)
            http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    
            http.csrf().disable();  //关闭csrf
        }
    

4、认证成功后的处理

  • 创建认证成功后的处理器

    /**
     * 认证成功后的处理器
     */
    @Component
    public class SimpleAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    
        @Resource
        private RedisMapper redisMapper;
    
        @Override
        public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
                                            HttpServletResponse httpServletResponse,
                                            Authentication authentication) throws IOException, ServletException {
            //设置响应数据的编码
            httpServletResponse.setContentType("application/json;charset=UTF-8");
    
            //从security中取出保存的用户信息(帐号,权限)
           User user= (User) authentication.getPrincipal();
           //获得帐号
           String account= user.getUsername().split(",")[1];
           //获得权限
            Collection<GrantedAuthority> authoriteList= user.getAuthorities();
            String authorites= StringUtils.join(authoriteList,",");
    
            //生成jwsToken
            String jwsToken= JwtUtils.createJwtToken(user.getUsername());
    
            //获得用户的帐号和权限,保存到redis
            redisMapper.setKey(account+":login",jwsToken,30, TimeUnit.MINUTES );
            redisMapper.setKey(account+":author", authorites,30, TimeUnit.MINUTES );
    
            //返回
            ResponseResult<String> responseResult=new ResponseResult<String>(2000, "OK", jwsToken);
    
            //输出json字符串到客户端
            PrintWriter printWriter =httpServletResponse.getWriter();
            printWriter.print(JacksonUtil.toJsonString(responseResult));
            printWriter.flush();
            printWriter.close();
        }
    }
    
  • 将认证成功处理器注册到认证过滤器中

      /**
         * security过滤器链操作
         * @param http
         * @throws Exception
         */
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            //鉴权相关的过滤器  暂时不管
            http.authorizeRequests()
                    .anyRequest()
                    .authenticated();   // 1
    
            //配置与登录相关的过滤器
            http.formLogin()
                    .successHandler(simpleAuthenticationSuccessHandler) //认证成功后的处理地器
                    .failureHandler(simpleAuthenticationFailureHandler)  //认证失败后的处理器
                    .permitAll();//这句配置很重要,新手容易忘记。放开 login.html和dologin 的访问权
    
            //关闭security  session(使用redis存用户的认证token)
            http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    
            http.csrf().disable();  //关闭csrf
        }
    

5、security认证流程【重点】

  • 当用户发送的请求中带有login字符串,会被UsernamePasswordAuthenticationFilter过滤器拦截
  • 拦截到登录请求会会获得用户输入的帐号和密码并保存到AuthenticationManager。
  • AuthenticationManager将用户输入的帐号传给UserDetailsService,通过UserDetailsService获得用户正确的信息(密码,权限)。封装到UserDetails对象中返回给AuthenticationManager
  • “表面上” AuthenticationManager的做认证和鉴权比对工作的那个人,它是认证和鉴权比对工作的起点。实际上是由AuthenticationProvider 接口的实现类ProvierderManager

6、退出处理器

  • 创建退出成功处理器

    /**
     * 退出成功处理器
     */
    @Component
    public class SimpLogoutSuccessHandler implements LogoutSuccessHandler {
    
        @Resource
        private RedisMapper redisMapper;
    
        @Override
        public void onLogoutSuccess(HttpServletRequest httpServletRequest,
                                    HttpServletResponse httpServletResponse,
                                    Authentication authentication) throws IOException, ServletException {
            //设置响应数据的编码
            httpServletResponse.setContentType("application/json;charset=UTF-8");
    
            //获得请求头
            String jwsToken = httpServletRequest.getHeader("jwsToken");
            //解析
            String str = JwtUtils.getUserNameFormJwt(jwsToken);
            String account = str.split(",")[1];
            
            //删除redis中的登录和权限token
            redisMapper.delKey(account + ":login");
            redisMapper.delKey(account + ":author");
            
            ResponseResult<Void> responseResult = new ResponseResult<Void>(2000, "退出系统成功");
            //输出json字符串到客户端
            PrintWriter printWriter = httpServletResponse.getWriter();
            printWriter.print(JacksonUtil.toJsonString(responseResult));
            printWriter.flush();
            printWriter.close();
        }
    }
    
  • 修改security配置类

    /**
         * security过滤器链操作
         * @param http
         * @throws Exception
         */
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            //鉴权相关的过滤器  暂时不管
            http.authorizeRequests()
                    .anyRequest()
                    .authenticated();   // 1
    
            //配置与登录相关的过滤器
            http.formLogin()
                    .successHandler(simpleAuthenticationSuccessHandler) //认证成功后的处理地器
                    .failureHandler(simpleAuthenticationFailureHandler)  //认证失败后的处理器
                    .permitAll();//这句配置很重要,新手容易忘记。放开 login.html和dologin 的访问权
    
            //退出操作
            http.logout().logoutSuccessHandler(simpleLogoutSuccessHandler);//退出成功处理器
    
            //关闭security  session(使用redis存用户的认证token)
            http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    
            http.csrf().disable();  //关闭csrf
        }
    

三、security鉴权

1、自定义过滤器对token续期

鉴权前需要判断用户是否登录过。续期

  • 创建自定义过滤器,判断用户是否登录过。登录过则续期。没有登录过则提示

    /**
     * 判断用户是否登录过
     */
    @Component
    public class JwtFilter extends OncePerRequestFilter {
    
        @Resource
        private RedisMapper redisMapper;
    
    
        @Override
        protected void doFilterInternal(HttpServletRequest request,
                                        HttpServletResponse response,
                                        FilterChain filterChain) throws ServletException, IOException {
            //判断当前请求是不是非登录请求
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if(authentication!=null){ //上下文中有AuthenticationToken表示当前是登录请求直接放行
                filterChain.doFilter(request, response);
                return;
            }
    
            /*  非登录请求,判断用户是否登录过  */
            String tokenStr=request.getHeader("jwsToken");
            // 请求头中没有token   未登录
            if(StringUtils.isEmpty(tokenStr)){ //请求中没有请求头,放行到下一个过滤器(异常过滤器)
                filterChain.doFilter(request, response);
                return;
            }
    
            //有token,验证token是否合法   不合法提示
            if(!JwtUtils.verify(tokenStr)){  //不合法
                filterChain.doFilter(request, response);
                return;
            }
    
            //合法,判断redis中token是否过期   不存在
              //解板出帐号
            String userStr=JwtUtils.getUserNameFormJwt(tokenStr);
            String account=userStr.split(",")[1];
            if(redisMapper.getKey(account+":login")==null){ //token已过期
                filterChain.doFilter(request, response);
                return;
            }
    
            //合法,与redis中不一样
            String redisToken= redisMapper.getKey(account+":login").toString();
            if(!tokenStr.equals(redisToken)){  //请求头中的token和redis中的token不一样
                filterChain.doFilter(request, response);
                return;
            }
    
            //合法,没过期 续期
            String authorites=redisMapper.getKey(account+":author").toString();
            redisMapper.setKey(account+":login",redisToken,30, TimeUnit.MINUTES );
            redisMapper.setKey(account+":author", authorites,30, TimeUnit.MINUTES );
    
            //将jwtToken转为usernamePasswordAuthenticationToker
            UsernamePasswordAuthenticationToken authenticationToken=new UsernamePasswordAuthenticationToken(userStr,redisToken,
                    AuthorityUtils.commaSeparatedStringToAuthorityList(authorites));
    
            //将security认证令牌添加到上下文中
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            filterChain.doFilter(request, response);//放行到下一个过滤器
        }
    }
    
  • 将自定义过滤器添加到security过滤器链中

    必须添加到UsernamePasswordAuthenticationFilter和ExceptionTranslationFilter之间

    /**
         * security过滤器链操作
         * @param http
         * @throws Exception
         */
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            //鉴权相关的过滤器  暂时不管
            http.authorizeRequests()
                    .anyRequest()
                    .authenticated();   // 1
    
            //配置与登录相关的过滤器
            http.formLogin()
                    .successHandler(simpleAuthenticationSuccessHandler) //认证成功后的处理地器
                    .failureHandler(simpleAuthenticationFailureHandler)  //认证失败后的处理器
                    .permitAll();//这句配置很重要,新手容易忘记。放开 login.html和dologin 的访问权
    
            //退出操作
            http.logout().logoutSuccessHandler(simpleLogoutSuccessHandler);//退出成功处理器
    
            //将JwtFilter添加以认证过滤器下面
            http.addFilterAfter(jwtFilter, UsernamePasswordAuthenticationFilter.class);
            
            //关闭security  session(使用redis存用户的认证token)
            http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    
            http.csrf().disable();  //关闭csrf
        }
    

2、鉴权异常处理

FilterSecurityInterceptor 会抛出 2 种异常

  • 在用户 “该登录而未登录” 时,抛出 AuthenticationException 异常;默认情况下,抛出 AuthenticationException 异常时,Spring Security 返回 401 错误:未授权(Unauthorized)。

  • 在用户 “权限不够” 时,抛出 AccessDeniedException 异常。默认情况下,抛出 AccessDeniedException 异常时,Spring Security 返回 403 错误:被拒绝(Forbidden)访问

  • 创建认证异常处理器

    /**
     * 认证异常处理器
     */
    @Component
    public class SimpleAuthenticationEntryPoint implements AuthenticationEntryPoint {
        @Override
        public void commence(HttpServletRequest httpServletRequest,
                             HttpServletResponse httpServletResponse,
                             AuthenticationException e) throws IOException, ServletException {
            //设置响应数据的编码
            httpServletResponse.setContentType("application/json;charset=UTF-8");
    
            ResponseResult<Void> responseResult=new ResponseResult<Void>(4001, "未登录或token已过期");
            
            //输出json字符串到客户端
            PrintWriter printWriter =httpServletResponse.getWriter();
            printWriter.print(JacksonUtil.toJsonString(responseResult));
            printWriter.flush();
            printWriter.close();
        }
    }
    
  • 创建鉴权异常处理器

    /**
     * 鉴权异常
     */
    @Component
    public class SimpleAccessDeniedHandler implements AccessDeniedHandler {
        @Override
        public void handle(HttpServletRequest httpServletRequest,
                           HttpServletResponse httpServletResponse,
                           AccessDeniedException e) throws IOException, ServletException {
            //设置响应数据的编码
            httpServletResponse.setContentType("application/json;charset=UTF-8");
    
            ResponseResult<Void> responseResult=new ResponseResult<Void>(4003, "无此权限请联系管理员");
    
            //输出json字符串到客户端
            PrintWriter printWriter =httpServletResponse.getWriter();
            printWriter.print(JacksonUtil.toJsonString(responseResult));
            printWriter.flush();
            printWriter.close();
        }
    }
    
  • 修改security配置类

    /**
         * security过滤器链操作
         * @param http
         * @throws Exception
         */
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            //鉴权相关的过滤器  暂时不管
            http.authorizeRequests()
                    .anyRequest()
                    .authenticated();   // 1
    
            //配置与登录相关的过滤器
            http.formLogin()
                    .successHandler(simpleAuthenticationSuccessHandler) //认证成功后的处理地器
                    .failureHandler(simpleAuthenticationFailureHandler)  //认证失败后的处理器
                    .permitAll();//这句配置很重要,新手容易忘记。放开 login.html和dologin 的访问权
    
            //退出操作
            http.logout().logoutSuccessHandler(simpleLogoutSuccessHandler);//退出成功处理器
    
            //将JwtFilter添加以认证过滤器下面
            http.addFilterAfter(jwtFilter, UsernamePasswordAuthenticationFilter.class);
    
           //鉴权异常处理
            http.exceptionHandling()
                    .authenticationEntryPoint(simpleAuthenticationEntryPoint)  //认证异常
                    .accessDeniedHandler(simpleAccessDeniedHandler); //鉴权异常
            
            //关闭security  session(使用redis存用户的认证token)
            http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    
            http.csrf().disable();  //关闭csrf
        }
    

3、鉴权

在security中支持两种鉴权方式:配置类方式,注解

a、配置类方式【了解】

当前用户是否有权限访问某个 URI 的相关配置也是写在 configure(HttpSecurity http) 方法中。

.antMatchers() 方法是一个采用 ANT 风格的 URL 匹配器。

  • 使用 ? 匹配任意单个字符

  • 使用 * 匹配 0 或任意数量的字符

  • 使用 ** 匹配 0 或更多的目录

权限表达式说明
permitAll()永远返回 true
denyAll()永远返回 false
anonymous()当前用户是匿名用户(anonymous)时返回 true
rememberMe()当前用户是 rememberMe 用户时返回 true
authentication当前用户不是匿名用户时,返回 true
fullyAuthenticated当前用户既不是匿名用户,也不是 rememberMe 用户时,返回 true
hasRole(“role”)当用户拥有指定身份时,返回 true
hasAnyRole(“role1”, “role2”, …)当用户返回指定身份中的任意一个时,返回 true
hasAuthority(“authority1”)当用于拥有指定权限时,返回 true
hasAnyAuthority(“authority1”, “authority2”)当用户拥有指定权限中的任意一个时,返回 true
hasIpAddress(“xxx.xxx.x.xxx”)发送请求的 ip 符合指定时,返回 true
principal允许直接访问主体对象,表示当前用户

修改配置类

/**
     * security过滤器链操作
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //鉴权相关的过滤器  暂时不管
        http.authorizeRequests()
                .antMatchers("/goods/list").hasRole("EXPLOIT") //角色值必须大写,且前面不能加ROLE_
                .antMatchers("/goods/list").hasAuthority("goods:list")
                .antMatchers("/hello").anonymous() //匿名可访问
                .antMatchers("/hello2").permitAll() //不管有没有权限都可以访问
                .anyRequest()
                .authenticated();   // 1

        //配置与登录相关的过滤器
        http.formLogin()
                .successHandler(simpleAuthenticationSuccessHandler) //认证成功后的处理地器
                .failureHandler(simpleAuthenticationFailureHandler)  //认证失败后的处理器
                .permitAll();//这句配置很重要,新手容易忘记。放开 login.html和dologin 的访问权

        //退出操作
        http.logout().logoutSuccessHandler(simpleLogoutSuccessHandler);//退出成功处理器

        //将JwtFilter添加以认证过滤器下面
        http.addFilterAfter(jwtFilter, UsernamePasswordAuthenticationFilter.class);

        //鉴权异常处理
        http.exceptionHandling()
                .authenticationEntryPoint(simpleAuthenticationEntryPoint)  //认证异常
                .accessDeniedHandler(simpleAccessDeniedHandler); //鉴权异常

        //关闭security  session(使用redis存用户的认证token)
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.csrf().disable();  //关闭csrf
    }
b、注解方式【日常使用】

只需要在controller方法上添加鉴权注解即可。

security支持三套鉴权注解,默认都是关闭状态需要打开。

JSR-250 注解:基于角色的权限验证
	@RolesAllowed  拥有指定角色才能访问
	@DenyAll       匿名可访问
	@PermitAll     永远可访问
	
@Secured 注解是 jsr250 标准出现之前,Spring Security 框架自己定义的注解。基于角色的权限验证

PrePost 注解:   Spring Security 框架自己定义的注解
	@PreAuthorize("hasRole('USER')")  判断是否拥有指定角色
	@PreAuthorize("hasAuthority('user:insert')")  判断是否拥有指定权限
  • 开启security注解支持

    /**
     * spring security配置类(重点)
     */
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true) //开启PrePost注解
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    }
    
  • 修改controller

     @PostMapping("/goods/add")
        @PreAuthorize("hasAuthority('goods:add')")
        public ResponseResult<Goods> addGoods(@RequestBody Goods goods){
            return new ResponseResult<>(2000, goods);
        }
    

如果需要配置匿名访问,只能在配置类中进行

四、Swagger常用注解

  • @Api:写在controller类上,对类的说明

    @RestController
    @Api(tags = "商品API")
    public class GoodsController
    
  • @ApiOperation: 写在controller方法上,对方法进行说明

     @GetMapping("/goods/delete")
        @PreAuthorize("hasAuthority('goods:delete')")
        @ApiOperation(value = "删除商品",
                        notes = "根据id删除商品",
                        response =ResponseResult.class )
        public ResponseResult<Void> deleteGoods(Integer id)
        
        
    属性说明:
      value:方法说明标题
      notes:方法详细描述
      response:方法返回值类型
    
  • @ApiImplicitParams: 对方法的非对象形参进行说明

        @GetMapping("/goods/delete")
        @ApiImplicitParams(value = {
                @ApiImplicitParam(name="id", value = "商品id",
                        type = "Integer", required = false,
                        paramType = "query", defaultValue  = "0")
        })
        public ResponseResult<Void> deleteGoods(@RequestParam(value = "id",required = false,defaultValue = "0") Integer id)
            
    属性说明:
        name:形参名称
        value:形参的说明文字
        type:形参类型
        required:该参数是否必须  true|false
        paramType: 用于描述参数是以何种方式传递到 Controller 的,它的值常见有: path, query, body 和 header 
            path 表示参数是『嵌在』路径中的,它和 @PathVariable 参数注解遥相呼应;
        	query 表示参数是以 query string 的形式传递到后台的(无论是 get 请求携带在 url 中,还是 post 请求携带在请求体中),它和 @RequestParam 参数注解遥相呼应;
        	body 表示参数是以 json string 的形式携带在请求体中传递到后台的,它和 @RequestBody 参数注解遥相呼应;
        	header 表示参数是『藏在』请求头中传递到后台的,它和 @RequestHeader 参数注解遥相呼应的。
        	form 不常用。
        defaultValue :参数默认值
    
  • @ApiModel: 对方法的对象形参进行说明

    该注解是写在形参对象中

    • 修改形参实体类

      @ApiModel
      public class Goods implements Serializable {
          private static final long serialVersionUID = 1L;
      
          @ApiModelProperty(value = "商品编号,自增主键")
          private Integer id;
      
          /**
           * 商品名称
           */
          @ApiModelProperty(value = "商品名称")
          private String goodsName;
      
          /**
           * 单价
           */
          @ApiModelProperty(value = "商品单价")
          private BigDecimal price;
      }
      
  • ApiResponse: 用于方法的返回值说明

     @PostMapping("/goods/add")
        @PreAuthorize("hasAuthority('goods:add')")
        @ApiOperation(value = "添加商品",
                notes = "添加商品",
                response =ResponseResult.class )
        @ApiResponses({
                @ApiResponse(code = 2000, message = "添加成功", response = ResponseResult.class),
                @ApiResponse(code = 5000, message = "添加失败", response = ResponseResult.class)
        })
        public ResponseResult<Goods> addGoods(@RequestBody Goods goods)
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值