自定义认证过滤器和自定义授权过滤器
目录
通过数据库动态加载用户信息
具体实现步骤
一.创建数据库
二.编写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应用中配置和发送邮件,帮助开发者快速掌握这一关键技…...
iPhone/iPad技巧:如何解锁锁定的 iPhone 或 iPad
“在我更新 iPhone 上的软件后,最近我遇到了iPhone 被锁定到所有者的消息,该如何解决?” 根据我们的研究,许多用户在 iOS 18 更新或恢复出厂设置后都会遇到同样的问题。只要出现问题,您就无法使用 iPhone 或 第 1 部分…...
无源码实现免登录功能
因项目要求需要对一个没有源代码的老旧系统实现免登录功能,系统采用前后端分离的方式部署,登录时前端调用后台的认证接口,认证接口返回token信息,然后将token以json的方式存储到cookie中,格式如下: 这里有…...
大数据毕业设计选题推荐-民族服饰数据分析系统-Python数据可视化-Hive-Hadoop-Spark
✨作者主页:IT研究室✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…...
疾风大模型气象,基于气象数据打造可视化平台
引言 随着气象数据的广泛应用,越来越多的行业依赖天气预报与气候分析来做出决策。从农业、航空、能源到物流,气象信息无时不刻影响着各行各业的运作。然而,气象数据本身复杂且多样,如何将这些数据转化为直观、易于理解的图形和信…...
PHP安装后Apache无法运行的问题
问题 按照网上教程php安装点击跳转教程,然后修改Apache的httpd.conf文件,本来可以运行的Apache,无法运行了 然后在"C:\httpd-2.4.62-240904-win64-VS17\Apache24\logs\error.log"(就是我下载Apache的目录下的logs中&am…...
[论文精读]Multi-Channel Graph Neural Network for Entity Alignment
论文网址:Multi-Channel Graph Neural Network for Entity Alignment (aclanthology.org) 论文代码:https:// github.com/thunlp/MuGNN 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&a…...
Study-Oracle-10-ORALCE19C-RAC集群搭建(一)
一、硬件信息及配套软件 1、硬件设置 RAC集群虚拟机:CPU:2C、内存:10G、操作系统:50G Openfile数据存储:200G (10G*2) 2、网络设置 主机名公有地址私有地址VIP共享存储(SAN)rac1192.168.49.13110.10.10.20192.168.49.141192.168.49.130rac2192.168.49.13210.10.10.3…...
1.8 物理层下的传输媒体
欢迎大家订阅【计算机网络】学习专栏,开启你的计算机网络学习之旅! 文章目录 1 导引型传输媒体1.1 双绞线1.2 同轴电缆1.3 光缆 2 非导引型传输媒体2.1 无线电微波通信2.2 多径效应2.3 卫星通信2.4 无线局域网 在数据通信系统中,传输媒体是发…...
指纹定位的原理与应用场景
目录 原理 1. 信号特征收集 2. 定位算法 推导公式 距离估算公式 定位算法公式 使用场景 发展前景 指纹定位是一种基于无线信号强度(如Wi-Fi、RFID、蓝牙等)来实现室内定位的技术。它借助于环境中多个基站的信号特征来推断用户的位置。以下是对指纹定位的详细讲解,包…...
发现一款适合所有用户小巧且强大的编辑器(完美替换Windows记事本)
文章目录 📖 介绍 📖🏡 演示环境 🏡📒 编辑器 📒📝 功能亮点📝 适用场景📝 安装使用📝 替换Windows记事本🎈 获取方式 🎈⚓️ 相关链接 ⚓️📖 介绍 📖 今天,发现一款小巧(仅1.26M)且功能强大的编辑器,适用于文本编辑,编程开发等,应该说是适…...
中国建设银行网站上不去/嘉定区整站seo十大排名
自我补充:ubuntu在线安装sqlite3数据库的方法: 系统平台:ubuntu12.04在ubuntu里面直接使用命令:sudo apt-get install sqlite3 ,出现:……………………libsqlite3-0 ( 3.7.9-2ubuntu1) but 3.7.9-2ubuntu1.1 is to be…...
去哪里学做网站app/爱站工具网
PHP调用MYSQL存储过程实例 标签: mysql存储phpsqlquerycmd2010-09-26 11:10 11552人阅读 评论(3) 收藏 举报实例一:无参的存储过程$conn mysql_connect(localhost,root,root) or die ("数据连接错误!!!");mysql_select_db(test,$conn);$sql …...
七牛云加速WordPress/济南seo网站优化
前段时间遇到了这个问题 也百度了很多 不过还是用自己的方法解决了 一个超级简单的方法 简单到令人发指 由于直接写文本太丑了 所以还是截图吧 嘻嘻嘻 假如有一个这样的数组 (这是假如 可能每个人的数据都不一样哦 但是方法都是可用的) 因为我这个项目功…...
云南省建设厅标准员网站/怎样建立自己网站
对于一些操作seo优化的小伙伴来说,一个网站真正通过首页进入的流量是占据总流量小部分,绝大部分的流量都是通过文章内容页面进入的网站,这部分的流量占据绝大多数部分,这个就是网站优化中的二八定律。那么我们在网站seo优化中文章…...
南宁美容网站建设/北京seo做排名
目录 1.Spring家族 2.IOC 2.1IOC原理 DI 依赖注入(DI)的方式: IOC的优势 IOC运行流程 SpringIOC支持的功能 2.2SpringIOC的应用 2.2.1BeanDefinitionRegistry 2.2.2BeanFactory 2.2.3ApplicationContext 2.2.4Bean是如何装载在s…...
如何用自己电脑做网站/长春seo招聘
目录13.1、枚举类13.1.1、概述13.1.2、自定义枚举类13.1.2.1、第一版13.1.2.2、第二版13.1.2.3、第三版13.1.2.4、测试方法13.1.3、系统的枚举类13.1.3.1、第一版13.1.3.2、第二版13.1.3.3、第三版13.1.3.4、测试方法13.1.4、常见方法13.1.5、注意事项13.2、注解13.2.1、概述13…...