自定义认证过滤器和自定义授权过滤器
目录
通过数据库动态加载用户信息
具体实现步骤
一.创建数据库
二.编写secutity配置类
三.编写controller
四.编写服务类实现UserDetailsService接口类
五.debug springboot启动类
认证过滤器
SpringSecurity内置认证流程
自定义认证流程
第一步:自定义一个类继承AbstractAuthenticationProcessingFilter类
第二步:把自定义的认证过滤器交给spring管理,并配置认证的路径
第三步:把自定义的认证过滤器配置在默认认证过滤器之前
第四步:测试
基于JWT实现无状态认证
第一步:编写生成jwt票据工具类
第二步:在认证成功后执行的方法里生成jwt令牌并返回给前端
第三步:结果
SpringSecurity基于Jwt实现认证小结
授权过滤器
授权流程
自定义授权过滤器流程
第一步:自定义一个类继承 OncePerRequestFilter类
第二步:把这个授权过滤器交给spring管理
第三步:测试
自定义权限拒绝处理
自定义认证用户权限拒绝处理器
自定义匿名用户拒绝处理器
在配置类中添加这两个处理器
测试:
1.用户没有权限访问接口资源
2.匿名用户访问接口资源(把token票据请求头删除)
通过数据库动态加载用户信息
使用SpringSecurity时,访问某一个资源路径时,SpringSecurity会自动拦截,并跳转到登录页面(SpringSecurity提供),登录之后才可以访问指定的资源。
登录的这个过程就是用户的认证
认证的具体过程就是:Spring Security底层会自动调用UserDetailsService类型bean提供的用户信息与前端返回的用户信息进行合法比对,如果比对成功则资源放行,否则就认证失败;
所以我们需要创建一个UserDetailsService对象bean给spring容器管理
具体的步骤就是需要创建一个类实现UserDetailsService接口,重写loadUserByUsername()方法,
UserDetails loadUserByUsername(String userName)用户认证时自动调用这个方法,userName就是前端输入的明文密码
调用这个方法的目的就是加载一个用户类,让SpringSecurity底层拿去与前端返回的用户信息进行比对
在这个方法里面根据用户名去数据库查找这个用户的信息(用户名,用户密文密码,用户拥有的权限集合),然后封装到User类(实现了UserDetails接口)对象里,最后返回即可。
返回的这个用户信息就会被SpringSecurity底层自动调用去与前端返回的用户信息进行合法比对
总的来说就是:用户的认证是先调用loadUserByUsername方法,根据前端返回的用户名根据数据库查找这个用户的详细信息(用户名,用户密码密文,用户的权限集合),然后封装成一个UserDetails类,然后Spring Security底层再自动调用UserDetailsService类型bean提供的用户信息与前端返回的用户信息进行合法比对,如果比对成功则资源放行,否则就认证失败;
用户类中的是用户的密文密码,而前端返回的用户密码是密文,SpringSecurity要怎么比对呢
所以我们要配置BCryptPasswordEncoder加密相关bean,底层会自动调用这个bean把密码密文与密码明文进行匹配,所以我们存入数据库的密码密文需要使用BCrypt算法加密
具体实现步骤
一.创建数据库
create database security_demo default charset=utf8mb4;
use security_demo;CREATE TABLE `tb_user` (`id` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(100) DEFAULT NULL,`password` varchar(100) DEFAULT NULL,`roles` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;INSERT INTO `tb_user` VALUES (1, 'hhh', '$2a$10$f43iK9zKD9unmgLao1jqI.VluZ.Rr/XijizVEA73HeOu9xswaUBXC', 'ROLE_ADMIN,P5');
INSERT INTO `tb_user` VALUES (2, 'aaa', '$2a$10$f43iK9zKD9unmgLao1jqI.VluZ.Rr/XijizVEA73HeOu9xswaUBXC', 'ROLE_SELLER,P7,ROLE_ADMIN');
密码都是123456经过BCrypt算法加密后的
二.编写secutity配置类
@Configuration
@EnableWebSecurity//开启web安全设置生效
//开启SpringSecurity相关注解支持
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {//TODO:返回一个BCryptPasswordEncoder密码加密类类,让SpringSecurity自动调用把密码密文和明文进行匹配@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin()//开启默认form表单登录方式.and().logout()//登出用默认的路径登出 /logout.permitAll()//允许所有的用户访问登录或者登出的路径.and().csrf().disable()//启用CSRF,防止CSRF攻击.authorizeRequests();//授权方法,该方法后有若干子方法进行不同的授权规则处理/* //允许所有账户都可访问(不登录即可访问),同时可指定多个路径.antMatchers("/register").permitAll()//允许所有的用户访问.antMatchers("/hello").hasAuthority("P1") //具有P5权限才可以访问.antMatchers("/say").hasRole("SELECT") //具有ROLE_ADMIN 角色才可以访问,会自动加上ROLE_.antMatchers("/aa","/bb").hasAnyAuthority("P1","ROLE_SELECT")//有任意一个权限都可以访问.antMatchers("/aa","/bb").hasAnyRole("SELECT")//有任意一个权限都可以访问.antMatchers("/aa","/bb").hasIpAddress("192.168.xxx.xxx")//必须是192.168.地址才能访问.antMatchers("/aa","/bb").denyAll()//任何用户都不可以访问.anyRequest().authenticated(); //除了上边配置的请求资源,其它资源都必须授权才能访问*/}
}
三.编写controller
使用注解给每个资源接口授权,拥有指定权限才能进行访问
@RestController
public class UserController {//拥有ROLE_ADMIN权限的用户才能访问此接口@PreAuthorize("hasRole('ADMIN')")@GetMapping("/hello")public String hello(){return "hello security";}//拥有ROLE_SELECT权限的用户才能访问此接口@PreAuthorize("hasRole('SELECT')")@GetMapping("/say")public String say(){return "say security";}@PermitAll//任何用户都可以访问此接口,不需要进行认证@GetMapping("/register")public String register(){return "register security";}
}
四.编写服务类实现UserDetailsService接口类
注意:
1.User是UserDetails接口的实现类,封装了用户权限相关的的数据及用户的权限数据, 不要导错包 ;
2.工程已经配置好了BCryptPasswordEncoder加密相关bean,底层会自动调用;
/*** 创建一个UserDetailsService类bean,用户认证时自动调用loadUserByUsername方法加载用户信息* 之后SpringSecurity会使用这个用户类与前端返回的用户信息进行合法比对,成功才能放行资源*/
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {@Autowiredprivate TbUserMapper tbUserMapper;/*** 通过用户名去数据库获取这个用户的具体信息(用户名,用户密文密码,用户的权限集合)创建一个用户类* @param userName 前端返回的用户名字* @return 返回一个用户类* @throws UsernameNotFoundException 用户不存在的异常*/@Overridepublic UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {//根据用户名去数据库查找用户的基本信息(用户名,密文密码,用户的权限集合)TbUser tbUser = tbUserMapper.findByUserName(userName);if(tbUser==null){throw new UsernameNotFoundException("该用户不存在");}//封装UserDetails类,User是UserDetails接口的实现类//使用工具类把用户的权限使用逗号进行分割List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList(tbUser.getRoles());UserDetails user = User.builder().username(tbUser.getUsername()).password(tbUser.getPassword()).authorities(authorities).build();//返回这个用户类,之后SpringSecurity会使用这个用户类与前端的返回的用户信息进行比对//自动调用BCryptPasswordEncoder bean把用户明文密码与用户密文密码进行比对return user;}
}
五.debug springboot启动类
访问hello接口(拥有ROLE_ADMIN权限才能访问),会自动跳到springsecurity提供的用户认证界面

会跳转到loadUserByUsername方法,并获取前端的用户名,然后使用这个用户名创建一个用户类
认证通过,即springSecurity会自动调用BCryptPasswordEncoder加密相关bean把密文密码和明文密码继续比对,比对通过就是认证通过,又因为hhh用户拥有ROLE_ADMIN权限,所以可以访问

认证过滤器
SpringSecurity内置认证流程

核心流程梳理如下:
- 认证过滤器(UsernamePasswordAuthentionFilter)接收form表单提交的账户、密码信息,并封装成UsernamePasswordAuthenticationToken认证凭对象;
- 认证过滤器调用认证管理器AuthenticationManager进行认证处理;
- 认证管理器通过调用用户详情服务获取用户详情UserDetails;
- 认证管理器通过密码匹配器PasswordEncoder进行匹配,如果密码一致,则将用户相关的权限信息一并封装到Authentication认证对象中;
- 认证过滤器将Authentication认证过滤器放到认证上下文,方便请求从上下文获取认证信息;
自定义认证流程
SpringSecurity内置的认证过滤器是基于post请求且为form表单的方式获取认证数据的,那如何接收前端Json异步提交的数据据实现认证操作呢?
显然,我们可仿照UsernamePasswordAuthentionFilter类自定义一个过滤器并实现认证过滤逻辑;
第一步:自定义一个类继承AbstractAuthenticationProcessingFilter类
在这个类中我们要重写 attemptAuthentication()方法,在这个方法中把前端收到的用户名,用户明文密码封装成 UsernamePasswordAuthenticationToken票据对象中,然后调用认证管理器认证这个用户的信息(就是使用UserDetailsService返回的用户类的密文密码通过密码匹配与用户明文密码进行比对)
我们还有重写一个构造器,在这个构造器中确定认证登录的地址
/*** 自定义过滤器,继承AbstractAuthenticationProcessingFilter类*/
public class MyUserNamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {private static final String USER_NAME="username";private static final String PASSWORD="password";/*** 自定义构造器,传入认证登录的url地址* @param loginUrl 登录的url地址*/public MyUserNamePasswordAuthenticationFilter(String loginUrl) {super(loginUrl);}/*** 尝试去认证的方法,把前端返回的用户名和密码封装到UsernamePasswordAuthenticationToken认证凭对象;* data:{"username":"hhh","password":"123456"}* @param request* @param response* @return* @throws AuthenticationException* @throws IOException* @throws ServletException*/@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {//判断请求方法必须是post提交,且提交的数据的内容必须是application/json格式的数据if (!request.getMethod().equalsIgnoreCase("POST") || !MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(request.getContentType())){throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());}//获取前端传入的json数据,并解析成map集合//获取请求参数//获取reqeust请求对象的发送过来的数据流ServletInputStream in = request.getInputStream();//将数据流中的数据反序列化成MapHashMap<String,String> loginInfo = new ObjectMapper().readValue(in, HashMap.class);String username = loginInfo.get(USER_NAME);username = (username != null) ? username : "";username = username.trim();String password = loginInfo.get(PASSWORD);password = (password != null) ? password : "";//将用户名和密码信息封装到认证票据对象下UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);//调用认证管理器认证指定的票据对象return this.getAuthenticationManager().authenticate(authRequest);}/*** 认证成功后执行的方法* @param request* @param response* @param chain* @param authResult* @throws IOException* @throws ServletException*/@Overrideprotected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {//获取封装后的用户对象
//这一个principal对象里的密码为nullUser principal=(User)authResult.getPrincipal();String username = principal.getUsername();Collection<GrantedAuthority> authorities = principal.getAuthorities();//设置返回数据格式response.setContentType(MediaType.APPLICATION_JSON_VALUE);//设置返回数据编码格式response.setCharacterEncoding("UTF-8");Map<String,String> info=new HashMap<>();info.put("msg","认证成功");info.put("code","1");info.put("data","");//把类对象变成json数据String jsonData = new ObjectMapper().writeValueAsString(info);//将json数据返回response.getWriter().write(jsonData);}/*** 认证失败执行的方法* @param request* @param response* @param failed* @throws IOException* @throws ServletException*/@Overrideprotected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {//设置响应数据为jsonresponse.setContentType(MediaType.APPLICATION_JSON_VALUE);//设置响应数据格式response.setCharacterEncoding("UTF-8");//设置响应的数据Map<String,String>info=new HashMap<>();info.put("msg","认证失败");info.put("code","0");info.put("data","");//将数据封装成json数据String jsonData = new ObjectMapper().writeValueAsString(info);response.getWriter().write(jsonData);}
}
第二步:把自定义的认证过滤器交给spring管理,并配置认证的路径
在这个自定义的认证过滤器bean中,需要注入一个认证管理器的bean
/*** 自定义认证过滤器* 隐含:如果认证成功,则在安全上下文中维护认证相关信息* 如果安全上下文中存在认证相关信息,则默认的UserNamePasswordAuthenticationFilter认证过滤器就不会执行* 所以执行顺序,自定义的过滤器在前,默认的过滤器在后* @return* @throws Exception*/@Beanpublic MyUserNamePasswordAuthenticationFilter myUserNamePasswordAuthenticationFilter() throws Exception {//构造认证过滤器对象,并设置认证路径 /myLoginMyUserNamePasswordAuthenticationFilter myUserNamePasswordAuthenticationFilter = new MyUserNamePasswordAuthenticationFilter("/myLogin");//注入一个认证管理器beanmyUserNamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManagerBean());return myUserNamePasswordAuthenticationFilter;}
第三步:把自定义的认证过滤器配置在默认认证过滤器之前
因为只要自定义的认证过滤器认证成功,就会在安全上下文中维护认证成功的相关信息,这样就不会去执行默认的认证过滤器
@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin()//开启默认form表单登录方式.and().logout()//登出用默认的路径登出 /logout.permitAll()//允许所有的用户访问登录或者登出的路径.and().csrf().disable()//启用CSRF,防止CSRF攻击.authorizeRequests();//授权方法,该方法后有若干子方法进行不同的授权规则处理//对于自定义过滤器,需要创建一个实例,所以用方法返回一个自定义认证过滤器http.addFilterBefore(myUserNamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);/* //允许所有账户都可访问(不登录即可访问),同时可指定多个路径.antMatchers("/register").permitAll()//允许所有的用户访问.antMatchers("/hello").hasAuthority("P1") //具有P5权限才可以访问.antMatchers("/say").hasRole("SELECT") //具有ROLE_ADMIN 角色才可以访问,会自动加上ROLE_.antMatchers("/aa","/bb").hasAnyAuthority("P1","ROLE_SELECT")//有任意一个权限都可以访问.antMatchers("/aa","/bb").hasAnyRole("SELECT")//有任意一个权限都可以访问.antMatchers("/aa","/bb").hasIpAddress("192.168.xxx.xxx")//必须是192.168.地址才能访问.antMatchers("/aa","/bb").denyAll()//任何用户都不可以访问.anyRequest().authenticated(); //除了上边配置的请求资源,其它资源都必须授权才能访问*/}
第四步:测试
访问myLogin接口

基于JWT实现无状态认证
JWT是无状态的,所以在服务器端无需存储和维护认证信息,这样会大大减轻服务器的压力,所以我们可在自定义的认证过滤器认证成功后通过successfulAuthentication方法向前端颁发token认证字符串;
第一步:编写生成jwt票据工具类
public class JwtTokenUtil {// Token请求头public static final String TOKEN_HEADER = "authorization";// Token前缀public static final String TOKEN_PREFIX = "Bearer ";// 签名主题public static final String SUBJECT = "JRZS";// 过期时间,单位毫秒public static final long EXPIRITION = 1000 * 60 * 60* 24 * 7;// 应用密钥public static final String APPSECRET_KEY = "hhha";// 角色权限声明private static final String ROLE_CLAIMS = "role";/*** 生成Token*/public static String createToken(String username,String role) {Map<String,Object> map = new HashMap<>();map.put(ROLE_CLAIMS, role);String token = Jwts.builder()a.setSubject(username).setClaims(map).claim("username",username).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + EXPIRITION)).signWith(SignatureAlgorithm.HS256, APPSECRET_KEY).compact();return token;}/*** 校验Token*/public static Claims checkJWT(String token) {try {final Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();return claims;} catch (Exception e) {e.printStackTrace();return null;}}/*** 从Token中获取用户名*/public static String getUsername(String token){Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();return claims.get("username").toString();}/*** 从Token中获取用户角色*/public static String getUserRole(String token){Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();return claims.get("role").toString();}/*** 校验Token是否过期*/public static boolean isExpiration(String token){Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();return claims.getExpiration().before(new Date());}
}
第二步:在认证成功后执行的方法里生成jwt令牌并返回给前端
/*** 认证成功后执行的方法* @param request* @param response* @param chain* @param authResult* @throws IOException* @throws ServletException*/@Overrideprotected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {//获取封装后的用户对象//这一个principal对象里的密码为nullUser principal=(User)authResult.getPrincipal();String username = principal.getUsername();Collection<GrantedAuthority> authorities = principal.getAuthorities();//使用工具类生成jwt令牌,authorities.toString()把权限集合变成["P1","ROLE_ADMIN]类型//构建JwtToken 加入权限信息是为了将来访问时,jwt解析获取当前用户对应的权限,做授权的过滤String token = JwtTokenUtil.createToken(username, authorities.toString());//设置返回数据格式response.setContentType(MediaType.APPLICATION_JSON_VALUE);//设置返回数据编码格式response.setCharacterEncoding("UTF-8");Map<String,String> info=new HashMap<>();info.put("msg","认证成功");info.put("code","1");info.put("data",token);//把类对象变成json数据String jsonData = new ObjectMapper().writeValueAsString(info);//将json数据返回response.getWriter().write(jsonData);}
第三步:结果

SpringSecurity基于Jwt实现认证小结

授权过滤器
授权流程

自定义授权过滤器流程
第一步:自定义一个类继承 OncePerRequestFilter类
重写doFilterInternal方法,在这个方法中,获取请求头的token票据,并进行校验判断
/*** 定义授权过滤器,本质就是获取一切请求头的token信息,进行校验*/
public class AuthenticationFilter extends OncePerRequestFilter {/*** 过滤中执行的方法* @param request* @param response* @param filterChain 过滤器链* @throws ServletException* @throws IOException*/@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {//1.从请求头中获取token字符串String tokenStr = request.getHeader(JwtTokenUtil.TOKEN_HEADER);//2.合法性判断//2.1 判断票据信息是否为空if(StringUtils.isBlank(tokenStr)){//如果票据为空,则放行,但是此时安全上下文中没有认证成功的票据,后续的过滤器如果得不到票据,后面的认证过滤器会进行拦截filterChain.doFilter(request,response);return;}//2.2检查票据是否合法,解析失败Claims claims = JwtTokenUtil.checkJWT(tokenStr);if(claims==null){//票据不合法,直接让过滤器终止response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.setCharacterEncoding("UTF-8");Map<String,String> info=new HashMap<>();info.put("msg","票据无效,请重新认证");info.put("data","");info.put("code","0");//把数据序列化成json格式String jsonData = new ObjectMapper().writeValueAsString(info);response.getWriter().write(jsonData);return;}//2.3//获取用户名String username = JwtTokenUtil.getUsername(tokenStr);//获取用户权限集合 "["P1","ROLE_ADMIN"]"String roles = JwtTokenUtil.getUserRole(tokenStr);//解析用户权限字符串String stripStr = StringUtils.strip(roles, "[]");List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList(stripStr);//3.组装数据到UsernamePasswordAuthenticationToken票据对象中UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username,null,authorities);//4.将封装的认证票据存入security安全上下文中,这样后续的过滤器就可以直接从安全上下文中获取用户的相关权限信息//TODO:以线程为维度:当前访问结束,那么线程回收,安全上下文中的票据也会回收,下次访问时需要重新解析SecurityContextHolder.getContext().setAuthentication(token);//5.放行请求,后续的过滤器,比如:认证过滤器如果发现安全上下文中存在token票据对象,就不会进行重新认证filterChain.doFilter(request,response);}
}
第二步:把这个授权过滤器交给spring管理
让这个授权过滤器的优先级高于自定义的认证过滤器,因为如果之前已经认证过,请求头中就会存在token票据,授权过滤器就会解析token票据,并把用户名和用户权限封装到UsernamePasswordAuthenticationToken类对象中,然后把这个对象存入安全上下文中,这样一来后续的认证过滤器就不会重新认证,直接放行
/*** 维护一个授权过滤器bean,检查jwt票据是否有效,并做相关处理*/@Beanpublic AuthenticationFilter authenticationFilter(){return new AuthenticationFilter();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin()//开启默认form表单登录方式.and().logout()//登出用默认的路径登出 /logout.permitAll()//允许所有的用户访问登录或者登出的路径.and().csrf().disable()//启用CSRF,防止CSRF攻击.authorizeRequests();//授权方法,该方法后有若干子方法进行不同的授权规则处理//对于自定义过滤器,需要创建一个实例,所以用方法返回一个自定义认证过滤器http.addFilterBefore(myUserNamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);//配置授权过滤器,它是资源安全的屏障,优先级最高,所以需要在自定义的认证过滤器之前http.addFilterBefore(authenticationFilter(), MyUserNamePasswordAuthenticationFilter.class);
}
第三步:测试
先通过/myLogin路径进行用户认证获取token票据
一开始会被授权过滤器拦截,但是请求头中票据为空,授权过滤器就会放行,进入到认证过滤器(因为访问路径是/myLogin),然后获取票据信息

使用该票据访问其他资源,访问成功
授权过滤器解析请求头的票据,把解析到的用户名,用户权限信息存入UsernamePasswordAuthenticationToken类对象,然后再将对象存入安全上下文中,这样后面的过滤器就知道这个用户有哪些权限

自定义权限拒绝处理
当用户未认证访问资源或者认证成功后访问没有权限的资源时,响应给前端的信息不友好,我们可通过自定义权限访问拒绝的处理器来友好提醒用户;
自定义认证用户权限拒绝处理器
自定义一个类实现AccessDeniedHandler接口
/*** 自定义用户没有权限访问一个资源接口时要执行的处理器*/
public class MyAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {//向前端返回用户没有权限的信息response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.setCharacterEncoding("UTF-8");Map<String,String> info=new HashMap<>();info.put("msg","用户没有权限访问此接口");info.put("code","0");info.put("data","");//将数据序列化成json数据String jsonData = new ObjectMapper().writeValueAsString(info);//向前端响应此json数据response.getWriter().write(jsonData);}
}
自定义匿名用户拒绝处理器
自定义一个类实现AuthenticationEntryPoint接口
/*** 自定义一个匿名用户(此用户没有进行认证)拒绝的处理器*/
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.setCharacterEncoding("UTF-8");Map<String,String> info=new HashMap<>();info.put("msg","匿名用户无法访问这个资源,请先登录认证");info.put("code","0");info.put("data","");String jsonData = new ObjectMapper().writeValueAsString(info);response.getWriter().write(jsonData);}
}
在配置类中添加这两个处理器
@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin()//开启默认form表单登录方式.and().logout()//登出用默认的路径登出 /logout.permitAll()//允许所有的用户访问登录或者登出的路径.and().csrf().disable()//启用CSRF,防止CSRF攻击.authorizeRequests();//授权方法,该方法后有若干子方法进行不同的授权规则处理//对于自定义过滤器,需要创建一个实例,所以用方法返回一个自定义认证过滤器http.addFilterBefore(myUserNamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);//配置授权过滤器,它是资源安全的屏障,优先级最高,所以需要在自定义的认证过滤器之前http.addFilterBefore(authenticationFilter(), MyUserNamePasswordAuthenticationFilter.class);//配置用户没有权限时的处理器和匿名用户无法访问资源的处理器http.exceptionHandling().accessDeniedHandler(new MyAccessDeniedHandler()).authenticationEntryPoint(new MyAuthenticationEntryPoint());}
测试:
1.用户没有权限访问接口资源

2.匿名用户访问接口资源(把token票据请求头删除)

相关文章:
自定义认证过滤器和自定义授权过滤器
目录 通过数据库动态加载用户信息 具体实现步骤 一.创建数据库 二.编写secutity配置类 三.编写controller 四.编写服务类实现UserDetailsService接口类 五.debug springboot启动类 认证过滤器 SpringSecurity内置认证流程 自定义认证流程 第一步:自定义一个类继承Abstr…...
单节点集群的设置及数据写入
背景:elasticsearch单个node节点写入数据-CSDN博客 单个节点数据,如下设置参数, 在单节点集群中,设置 `gateway.recover_after_nodes` 通常是没有意义的,因为单节点集群只有一个节点,无法满足 `gateway.recover_after_nodes` 的条件。然而,如果你仍然想在单节点集群中…...
【Linux学习】【Ubuntu入门】1-2 新建虚拟机ubuntu环境
1.双击打开VMware软件,点击“创建新的虚拟机”,在弹出的中选择“自定义(高级)” 2.点击下一步,自动识别ubuntu光盘映像文件,也可以点击“浏览”手动选择,点击下一步 3.设置名称及密码后…...
自动驾驶系列—自动驾驶MCU架构全方位解析:从单核到多核的选型指南与应用实例
🌟🌟 欢迎来到我的技术小筑,一个专为技术探索者打造的交流空间。在这里,我们不仅分享代码的智慧,还探讨技术的深度与广度。无论您是资深开发者还是技术新手,这里都有一片属于您的天空。让我们在知识的海洋中…...
基于单片机多功能称重系统设计
** 文章目录 前言概要功能设计设计思路 软件设计效果图 程序文章目录 前言 💗博主介绍:✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对…...
PWA(Progressive web APPs,渐进式 Web 应用): manifest.json、 Service Worker
文章目录 引言I 什么是 PWA功能特性技术上分为三个部分安装应用II Web 应用清单将Web 应用清单文件链接到站点manifest.json字段说明III Service Worker( 缓存管理)IV 结合构建工具让项目支持 PWA应用使用插件vite-plugin-pwaworkbox-webpack-plugin插件扩展知识将 PWA 作为脱机…...
【学习笔记】手写 Tomcat 八
目录 一、NIO 1. 创建 Tomcat NIO 类 2. 启动 Tomcat 3. 测试 二、解析请求信息 三、响应数据 创建响应类 修改调用的响应类 四、完整代码 五、测试 六、总结 七、获取全部用户的功能 POJO 生成 POJO 1. 在 Dao 层定义接口 2. 获取用户数据 3. 在 Service 层定…...
24年九月份生活随笔
九月份最后一天,烈士纪念日。 上午看了一会儿直播,庄重的仪式,铭记先辈为新中国抛头颅洒热血,当今盛世,如您所愿。 郑州马拉松官方通告,今天十点公布直通,中签,候补结果。 看完直…...
[含文档+PPT+源码等]精品大数据项目-基于Django实现的高校图书馆智能推送系统的设计与实现
大数据项目——基于Django实现的高校图书馆智能推送系统的设计与实现背景,可以从以下几个方面进行详细阐述: 一、信息技术的发展背景 随着信息技术的飞速发展和互联网的广泛普及,大数据已经成为现代社会的重要资源。在大数据背景下…...
Leecode刷题之路第七天之整数反转
题目出处 07-整数反转 题目描述 个人解法 思路: 1.将整数转换为字符串 2.倒序输出字符串 3.兼容负数case 代码示例:(Java) public int reverse(int x) {Integer integer new Integer(x);String s integer.toString();Strin…...
SpringBoot项目 | 瑞吉外卖 | 短信发送验证码功能改为免费的邮箱发送验证码功能 | 代码实现
0.前情提要 之前的po已经说了单独的邮箱验证码发送功能怎么实现: https://blog.csdn.net/qq_61551948/article/details/142641495 这篇说下如何把该功能整合到瑞吉项目里面,也就是把原先项目里的短信发送验证码的功能改掉,改为邮箱发送验证…...
Windows暂停更新
目录 前言注册表设定参考 前言 不想Windows自动更新,同时不想造成Windows商店不可用,可以采用暂停更新的方案。 但是通过这里设定的时间太短了,所以我们去注册表设定。 注册表设定 win r 输入 regedit进入注册表 HKEY_LOCAL_MACHINE\SOFT…...
alpine安装docker踩坑记
文章目录 前言错误场景正确操作最后 前言 你好,我是醉墨居士,最近使用alpine操作系统上docker遇到了一些错误,尝试解决之后就准备输出一篇博客,帮助有需要的后人能够少踩坑,因为淋过雨所以想给别人撑伞 错误场景 我…...
使用openpyxl轻松操控Excel文件
目录 1. openpyxl 简介2. 安装与快速入门2.1 安装 openpyxl2.2 快速创建一个 Excel 文件2.3 读取 Excel 文件 3. openpyxl 的核心概念3.1 工作簿(Workbook)3.2 工作表(Worksheet)3.3 单元格(Cell)3.4 行与列…...
指定PDF或图片多个识别区域,识别区域文字,并批量对PDF或图片文件改名
常见场景 用户有大量图片/PDF文件,期望能按照图片/PDF中的某些文字对图片/PDF文件重命名。期望工具可以批量处理、离线识别(保证数据安全性)。手工操作麻烦。具体场景:用户有工程现场照片,订单,简历等PDF或…...
Web3中的跨链技术:实现无缝连接的挑战
Web3的到来为互联网带来了去中心化的愿景,而跨链技术则是实现这一愿景的关键。跨链技术旨在解决不同区块链之间的互操作性问题,使得用户和应用能够在多个区块链网络之间无缝地传输数据和价值。尽管这一技术具有广阔的前景,但在实现过程中仍面…...
词袋(Bag of Words, BoW)
词袋(Bag of Words, BoW)模型详解 词袋(BoW)是一种用于文本处理的特征提取方法,常用于自然语言处理(NLP)任务中。在BoW模型中,文本被表示为一个词的无序集合,而忽略了词…...
HTTP Status 404 - /brand-demo/selectAllServlet错误解决原因-Servlet/JavaWeb/IDEA
检查xml文件的包名有无错误检查html文件的url有无写错,是否与Servlet的urlPatterns一致检查Servlet的urlpattern有没有写错(如写成name),检查doPost、doGet是否正常运行 注:IDEA新建Servlet时,默认的WebServlet注解中name需要改urlPatterns&…...
宁夏众智科技OA办公系统存在SQL注入漏洞
漏洞描述 宁夏众智科技OA办公系统存在SQL注入漏洞 漏洞复现 POC POST /Account/Login?ACTIndex&CLRHome HTTP/1.1 Host: Content-Length: 45 Cache-Control: max-age0 Origin: http://39.105.48.206 Content-Type: application/x-www-form-urlencoded Upgrade-Insecur…...
Spring邮件发送:配置与发送邮件详细步骤?
Spring邮件发送教程指南?怎么用Spring邮件发送服务? Spring框架提供了强大的邮件发送支持,使得开发者能够轻松地在应用程序中集成邮件发送功能。AokSend将详细介绍如何在Spring应用中配置和发送邮件,帮助开发者快速掌握这一关键技…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...
PHP 8.5 即将发布:管道操作符、强力调试
前不久,PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5!作为 PHP 语言的又一次重要迭代,PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是,借助强大的本地开发环境 ServBay&am…...
【LeetCode】算法详解#6 ---除自身以外数组的乘积
1.题目介绍 给定一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O…...
