相关博客

  • 环境搭建
    https://hcshow.blog.csdn.net/article/details/117083416

  • 自定义登录逻辑
    https://hcshow.blog.csdn.net/article/details/122347403
    https://hcshow.blog.csdn.net/article/details/122347650

  • 自定义登录页面的退出页面
    https://hcshow.blog.csdn.net/article/details/122349702

  • 自定义表单请求参数
    https://hcshow.blog.csdn.net/article/details/122364737

  • 放行静态资源
    https://hcshow.blog.csdn.net/article/details/122364853

  • 自定义403页面
    https://hcshow.blog.csdn.net/article/details/122365642

  • 整个Jwt/前后端分离(重点)
    https://hcshow.blog.csdn.net/article/details/122554517
    https://blog.csdn.net/lianghecai52171314/article/details/122434478 (带代码)

原理

由默认用户登录得到SpringSecurity下用户登录的请求流程:

入门示例:使用SpringSecurity提供的登录页面,实现真正的登录功能

第一步:自定义UserDetailsService

@Service
public class WegoUserDetailsService implements UserDetailsService {@Resourceprivate MemberMapper memberMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//认证:根据用户名去查询对应的用户信息LambdaQueryWrapper<Member> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(Member::getUsername,username);Member member = memberMapper.selectOne(queryWrapper);//如果没有查询到用户抛出异常if(member == null){throw GlobalException.builder().code(401).msg("用户名或密码不正确").build();}// TODO: 授权:查询用户的权限信息// 将用户信息+权限信息封装成UserDetails对象LoginMemberDetails loginMemberDetails = new LoginMemberDetails();loginMemberDetails.setMember(member);return loginMemberDetails;}
}

第二步:自定义UserDetails:

public class LoginMemberDetails implements UserDetails {@Setterprivate Member member;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;}/*** 登录密码* @return*/@Overridepublic String getPassword() {return member.getPassword();}/*** 登录账户* @return*/@Overridepublic String getUsername() {return member.getUsername();}/*** 返回对象是否未过期* @return*/@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

第三步:测试

为了测试,修改待登录用户的密码为:{noop}明文密码,比如:

密码加密存储

默认使用的PasswordEncoder要求数据库中的密码格式为:{id}password。它会根据id去判断密码的加密方式,但是我们一般不会采用这种方式,所以就需要替换PasswordEncoder。
实际项目中推荐使用BCryptPasswordEncoder。只需要把BCryptPasswordEncoder对象注入到Spring容器中,SpringSecurity就会使用该PasswordEncoder进行密码加密。
创建SpringSecurity配置文件,在其中指定加密方式:

@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {/*** 密码加密器* @return*/@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}
}

将数据库中的密码改成加密后的内容,然后测试就可以了。

用户登录:不再使用默认的用户user+随机字符串登录,使用我们自己数据库中的用户名+密码登录。

SpringSecurity需要对自定义的登录接口放行,让用户可以直接访问到这个接口。
在登录接口中需要通过AuthenticationManager的authenticate()方法进行用户认证,故需要先在SpringSecurity配置文件中把AuthenticationManager注入容器。
认证成功后生成一个jwt,在登录成功后的响应中返回。
为了让用户下回请求时能够通过jwt快速识别出用户,我们将用户信息缓存到Redis中,将用户的id作为key。

第一步:自定义UserDetails类:

@Getter
@Setter
@ToString
public class WegoUserDetails implements UserDetails {private User user;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;}@Overridepublic String getPassword() {if(user == null){return null;}return user.getPassword();}@Overridepublic String getUsername() {if(user == null){return null;}return user.getUsername();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

第二步:自定义UserDatailsService

在其中实现根据用户名查找用户,并将用户的信息封装成我们自己的UserDatails对象并返回

@Service
public class WegoUserDetailsService implements UserDetailsService {@Resourceprivate UserService userService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = userService.getOne(Wrappers.<User>query().eq("username", username));//TODO:查询权限信息WegoUserDetails userDetails = new WegoUserDetails();userDetails.setUser(user);return userDetails;}
}

第三步:修改SpringSecurity配置类

在系统注入AuthenticationManager对象并放行登录接口user/login:

@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {/*** 认证用:最终调用DetailsService的具体的登录逻辑* @return* @throws Exception*/@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http//关闭csrf.csrf().disable()//禁用Session:不通过Sessin获取SecurityContext.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()//登录接口:匿名访问(未登录可以访问,登录的话不就能访问了).antMatchers("/user/login").anonymous() //放行//其他的任何请求都需要鉴权认证.anyRequest().authenticated();}
}

第四步:自定义登录接口:

登录接口实现功能:
1、 将用户名+密码封装成UsernamePasswordAuthenticationToken对象
2、 调用AuthenticationManager的authenticate()方法进行登录判断

@RestController
@RequestMapping("/user")
public class LoginController {@Resourceprivate AuthenticationManager authenticationManager;@PostMapping("/login")public Result login(@RequestBody User user) {UsernamePasswordAuthenticationToken passwordAuthenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());authenticationManager.authenticate(passwordAuthenticationToken);return ResultUtil.success();}
}

第五步:在SpringSecurity配置文件中注入加密类的对象:

/*** 密码加密器* @return*/
@Bean
public PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();
}

在测试类中,利用该对象生成密码并保存到数据库中

class FafuRbacApplicationTests {@Testpublic void fun() {PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();System.out.println(passwordEncoder.encode("1234"));}
}

第六步:测试

打开postman,在其中请求user/login,注意以post方式+application/json的形式请求。

返回token

第一步:准备JWT工具类:

@Slf4j
@Component
//@ConfigurationProperties(prefix = "jwt")
public class JwtUtil {/*** 携带JWT令牌的HTTP的Header的名称,在实际生产中可读性越差越安全*/@Getter@Value("${jwt.token}")private String token;/*** 为JWT基础信息加密和解密的密钥* 在实际生产中通常不直接写在配置文件里面。而是通过应用的启动参数传递,并且需要定期修改。*/@Value("${jwt.secret}")private String secret;/*** JWT令牌的有效时间,单位秒* - 默认2周*/@Value("${jwt.expiration}")private Long expiration;private static String getUUID() {String token = UUID.randomUUID().toString().replaceAll("-", "");return token;}/*** SecretKey 根据 SECRET 的编码方式解码后得到:* Base64 编码:SecretKey key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretString));* Base64URL 编码:SecretKey key = Keys.hmacShaKeyFor(Decoders.BASE64URL.decode(secretString));* 未编码:SecretKey key = Keys.hmacShaKeyFor(secretString.getBytes(StandardCharsets.UTF_8));*/private static SecretKey getSecretKey(String secret) {byte[] encodeKey = Decoders.BASE64.decode(secret);return Keys.hmacShaKeyFor(encodeKey);}/*** 用claims生成token** @param claims 数据声明,用来创建payload的私有声明,subject只是claims的一部分* @return token 令牌*/private JwtBuilder getJwtBuilder(Map<String, Object> claims) {SecretKey key = getSecretKey(secret);//SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256); //两种方式等价// 添加payload声明JwtBuilder jwtBuilder = Jwts.builder()// 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的.setClaims(claims)// 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。.setId(getUUID())// 你也可以改用你喜欢的算法,支持的算法详见:https://github.com/jwtk/jjwt#features// SignatureAlgorithm.HS256:指定签名的时候使用的签名算法,也就是header那部分.signWith(key, SignatureAlgorithm.HS256)// iat: jwt的签发时间.setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + this.expiration * 1000));return jwtBuilder;}/*** 生成Token令牌** @param username 用户名* @return 令牌Token*/public String generateToken(String username) {Map<String, Object> claims = new HashMap<>();claims.put("sub", username);claims.put("created", new Date());return getJwtBuilder(claims).compact();}public String generateToken(Serializable id) {Map<String, Object> claims = new HashMap<>();claims.put("sub", id);claims.put("created", new Date());return getJwtBuilder(claims).compact();}/*** 从token中获取数据声明claim** @param token 令牌token* @return 数据声明claim*/public Claims getClaimsFromToken(String token) {try {SecretKey key = getSecretKey(secret);Claims claims = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();return claims;} catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | IllegalArgumentException e) {log.error("token解析错误", e);throw new IllegalArgumentException("Token invalided.");}}/*** 从token中获取登录用户名** @param token 令牌* @return 用户名*/public String getSubjectFromToken(String token) {String subject;try {Claims claims = getClaimsFromToken(token);subject = claims.getSubject();} catch (Exception e) {subject = null;}return subject;}/*** 获取token的过期时间** @param token token* @return 过期时间*/public Date getExpirationFromToken(String token) {return getClaimsFromToken(token).getExpiration();}public String getUserRole(String token) {return (String) getClaimsFromToken(token).get("role");}/*** 判断token是否过期** @param token 令牌* @return 是否过期:已过期返回true,未过期返回false*/public Boolean isTokenExpired(String token) {Date expiration = getExpirationFromToken(token);return expiration.before(new Date());}/*** 验证令牌:判断token是否非法** @param token       令牌* @param username 用户名* @return 如果token未过期且合法,返回true,否则返回false*/public Boolean validateToken(String token, String username) {//如果已经过期返回falseif (isTokenExpired(token)) {return false;}String usernameFromToken = getSubjectFromToken(token);return username.equals(usernameFromToken);}}

第二步:修改LoginController

在login()方法中,当用户登录成功时,生成token,并放到返回结果中

@RestController
@RequestMapping("/user")
public class LoginController {@Resourceprivate JwtUtil jwtUtil;@Resourceprivate AuthenticationManager authenticationManager;@PostMapping("/login")public Result login(@RequestBody User user) {UsernamePasswordAuthenticationToken passwordAuthenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());Authentication authenticate = authenticationManager.authenticate(passwordAuthenticationToken);if(authenticate == null){return ResultUtil.error(401,"error");}user = ((WegoUserDetails)authenticate.getPrincipal()).getUser();String token = jwtUtil.generateToken("user:" + user.getId());return ResultUtil.success().addData("token",token);}
}

第三步:修改application.yml,其中添加jwt的配置信息:

jwt:# 为JWT基础信息加密和解密的密钥,长度需要大于等于43# 在实际生产中通常不直接写在配置文件里面。而是通过应用的启动参数传递,并且需要定期修改secret: oQZSeguYloAPAmKwvKqqnifiQatxMEPNOvtwPsCLasd# JWT令牌的有效时间,单位秒,默认2周expiration: 1209600token: Authorization

第四步:测试:

token认证过滤器

第一步:自定义过滤器

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {@Resourceprivate ByteRedisUtil<WegoUserDetails> wegoUserDetailsByteRedisUtil;@Resourceprivate JwtUtil jwtUtil;@Overrideprotected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {//1、获取用户请求时传递过来的tokenString token = httpServletRequest.getHeader("Authorization");if(token == null || token.length() == 0){//放行到过滤器链中的下一个过滤器过滤filterChain.doFilter(httpServletRequest,httpServletResponse);return;}//如果用户请求携带有token,解析tokenClaims claimsFromToken = null;try {claimsFromToken = jwtUtil.getClaimsFromToken(token);} catch (Exception e) {System.out.println("非法的token");e.printStackTrace();}//获取用户idString userId = claimsFromToken.getSubject();//根据用户id,构造出一个Authentication对象,给过滤器链中后面的Filter使用(过滤器链中处理的都是Authentication对象)UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userId,null,null);SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);//放行到过滤器链中的下一个过滤器过滤filterChain.doFilter(httpServletRequest,httpServletResponse);}}

第二步:在SpringSecurity配置类中配置自定义的过滤器

@Resource
private JwtAuthenticationFilter jwtAuthenticationFilter;@Override
protected void configure(HttpSecurity http) throws Exception {……//添加过滤器http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}

第三步:测试

1、 先登录,获取token
2、 将token配置到header中,请求任意一个资源

完善token认证过滤器

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {@Resourceprivate ByteRedisUtil<WegoUserDetails> wegoUserDetailsByteRedisUtil;@Overrideprotected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {//……..        //获取用户idString userId = claimsFromToken.getSubject();//从Redis中获取缓存的用户数据WegoUserDetails wegoUserDetails = wegoUserDetailsByteRedisUtil.get("user:" + userId);if(wegoUserDetails == null){System.out.println("无效的token");}//根据用户id,构造出一个Authentication对象,给过滤器链中后面的Filter使用(过滤器链中处理的都是Authentication对象)UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(wegoUserDetails,null,null);SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);//放行到过滤器链中的下一个过滤器过滤filterChain.doFilter(httpServletRequest,httpServletResponse);}}

异常处理

原理(套路性内容)

在SpringSecurity中,在认证或者授权的过程中出现的异常会被ExceptionTranslationFilter捕获到,在ExceptionTranslationFilter中会去判断这个异常是认证失败还是授权失败产生的:
  • 认证过程中出现的异常,会被封装成AuthenticationException,SpringSecurity会调用AuthenticationEntryPoint对象的方法处理这个异常
  • 授权过程中出现的异常,会被封装成AccessDeniedException,SpringSecurity会调用AccessDeniedHandler对象的方法处理这个异常
    所以,自定义异常处理,只需要自定义AuthenticationEntryPoint和AccessDeniedHanler,然后在SpringSecurity中进行配置即可。

Spring Security 中的异常主要分为两大类:一类是认证异常,另一类是授权相关的异常:

  • AuthenticationException 是在用户认证的时候出现错误时抛出的异常。系统用户不存在、被锁定、凭证失效、密码错误等认证过程中出现的异常都由 AuthenticationException 处理。主要的子类如图:

  • AccessDeniedException 主要是在用户在访问受保护资源时被拒绝而抛出的异常。主要是 CSRF 相关的异常和授权服务异常。主要的子类如图:

状态码:

  • 401 未授权状态
    HTTP 401 错误 - 未授权(Unauthorized) 一般来说该错误消息表明您首先需要登录(输入有效的用户名和密码)。 如果你刚刚输入这些信息,立刻就看到一个 401 错误,就意味着,无论出于何种原因您的用户名和密码其中之一或两者都无效(输入有误,用户名暂时停用,账户被锁定,凭证失效等) 。总之就是认证失败了。其实正好对应我们上面的 AuthenticationException 。

  • 403 被拒绝状态
    HTTP 403 错误 - 被禁止(Forbidden) 出现该错误表明您在访问受限资源时没有得到许可。服务器理解了本次请求但是拒绝执行该任务,该请求不该重发给服务器。并且服务器想让客户端知道为什么没有权限访问特定的资源,服务器应该在返回的信息中描述拒绝的理由。一般实践中我们会比较模糊的表明原因。 该错误对应了我们上面的 AccessDeniedException 。

示例

第一步:自定义认证失败处理器

/*** 认证失败处理器** @author hc*/
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {response.setStatus(200);response.setContentType("application/json");response.setCharacterEncoding("utf-8");String message = authException.getMessage();if (message == null){message = "登录失败";}String json = "{\"code\":\"401\",\"msg\":"+ message +"}";response.getWriter().write(json);}}

第二步:自定义授权失败处理器

/*** 授权失败处理器** @author hc*/
@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {response.setStatus(200);response.setContentType("application/json");response.setCharacterEncoding("utf-8");String message = accessDeniedException.getMessage();if (message == null){message = "您的权限不足";}String json = "{\"code\":\"403\",\"msg\":"+ message +"}";response.getWriter().write(json);}
}

第三步:在SpringSecurity配置文件中配置:

@Resource
private AuthenticationEntryPointImpl authenticationEntryPoint;
@Resource
private AccessDeniedHandlerImpl accessDeniedHandler;@Override
protected void configure(HttpSecurity http) throws Exception {……//处理异常处理器http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint) //认证失败处理器.accessDeniedHandler(accessDeniedHandler) //授权失败处理器;
}

第四步:测试。

1、 将Redis中的用户删除,然后请求某个资源

2、 错误的用户名or密码登录

退出登录

在控制器中提供一个退出登录的接口,然后获取SecurityContextHolder中的认证信息,最后删除Redis中对应的数据即可。

代码实现

@RestController
@RequestMapping("/user")
public class LogoutController {@Resourceprivate ByteRedisUtil<WegoUserDetails> redisUtil;@GetMapping("/logout")public String logout(){//获取SecurityContextHolder中的用户idUsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();WegoUserDetails userDatails = (WegoUserDetails) authentication.getPrincipal();Long userId = userDatails.getUser().getId();//从Redis缓存中删除指定id的用户redisUtil.del(GlobalConst.REDIS_USER_KEY+userId);return "{\"code\":200,\"msg\":\"注销成功!\"}";}
}

测试

1、 正确用户名和密码登录,发现redis中缓存了数据
2、 访问某个资源,ok
3、 退出登录,发现Redis中缓存的数据没有了
4、 再次访问相同的资源,发现访问不到了:

授权

原理

SpringSecurity使用默认的FilterSecurityInterceptor进行权限校验。FilterSecurityInterceptor会从SecurityContextHolder获取 Authentication,然后获取其中的权限信息,从而知道当前用户是否拥有访问指定资源所需要的权限。
所以我们需要将当前登录用户的权限信息也存入到Authenticaion中。
只需要将用户的“权限+角色”信息存放到Authentication(在过滤器链中处理的对象)中,SpringSecurity就能自动处理权限验证

示例

准备工作

第一步:在RoleMapper.java接口中添加如下根据用户id获取该用户拥有的role的方法:
/*** 查询指定id的用户的role* @param userId* @return 角色名称-角色编码*/
@Select("""SELECT tb_role.id, tb_role.`code`FROM tb_role INNER JOIN tb_user_roleON tb_role.id = tb_user_role.role_idWHERE tb_user_role.user_id = #{userId}""")
List<Role> selectRoleCodeByUserId(@Param("userId") Long userId);
第二步:在PermissionMapper.java接口中添加如下根据Role的id获取该Role拥有的Permission的信息:
@Select("""SELECT tb_permission.`code`FROM tb_role_permission INNER JOIN tb_permissionON tb_role_permission.permission_id = tb_permission.idWHEREtb_role_permission.role_id = #{roleId,jdbcType=BIGINT}""")
List<String> selectPermissionCodeByRoleId(@Param("roleId") Long roleId);
第三步:在UserServiceImpl类中添加如下根据用户id获取该用户拥有的角色与权限的方法:
@Resource
private RoleMapper roleMapper;
@Resource
private PermissionMapper permissionMapper;@Override
public Set<String> getRolePermissionCodeByUserId(Long userId){Set<String> rolePermissionSet = new HashSet<>();根据用户id查询角色codeList<Role> roleList = roleMapper.selectRoleCodeByUserId(userId);for (Role role : roleList) {rolePermissionSet.add("ROLE_"+role.getCode());// 根据角色id查询权限codeList<String> permissionList = permissionMapper.selectPermissionCodeByRoleId(role.getId());for (String permission : permissionList) {rolePermissionSet.add(permission);}}return rolePermissionSet;
}

具体实现

第一步:修改UserDetails类,在其中添加如下代码:
//用户角色+权限
private Set<String> list;public WegoUserDetails(User user, Set<String> list) {this.user = user;this.list = list;
}private List<SimpleGrantedAuthority> authorities;@Override
public Collection<? extends GrantedAuthority> getAuthorities() {if(authorities == null) {authorities = list.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());}return authorities;
}
第二步:修改UserDetailsService类,在其中添加获取用户权限的信息:
@Service
public class WegoUserDetailsService implements UserDetailsService {@Resourceprivate UserService userService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//认证:根据用户名去查询对应的用户信息//Wrappers.<User>lambdaQuery().eq(User::getUsername,username);LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(User::getUsername, username);User user = userService.getOne(queryWrapper);//如果没有查询到用户抛出异常if (user == null) {throw GlobalException.builder().code(401).msg("用户名或密码不正确").build();}//TODO: 授权:查询用户的权限信息//List<String> set = List.of("add", "get");Set<String> set = userService.getRolePermissionCodeByUserId(user.getId());// 将用户信息+权限信息封装成UserDetails对象WegoUserDetails wegoUserDetails = new WegoUserDetails(user, set);return wegoUserDetails;}}
第三步:修改JwtAuthenticationFilter,在其中添加获取用户角色+权限的功能:
//TODO: 权限信息
Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
//此处必须使用三个参数的构造方法,其中第三个参数是权限信息
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, authorities);
第四步:测试
  • 第一步:开启相关配置:
@EnableGlobalMethodSecurity(prePostEnabled = true)
  • 第二步:在方法上添加注解
@RestController
public class TestController {@PreAuthorize("hasAuthority('sys:user:view')")@GetMapping("/fun1")public Result get() {return ResultUtil.success("sys:user:view");}@PreAuthorize("hasAuthority('sys:user:list')")@GetMapping("/fun2")public Result add() {return ResultUtil.success("sys:user:list");}@PreAuthorize("hasRole('admin')")@GetMapping("/fun3")public Result del() {return ResultUtil.success("admin");}}

设置SpringSecurity跨域

第一步:设置SpringBoot允许跨域

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry//设置允许跨域的路径.addMapping("/**")//设置允许路霸请求的域名.allowedOriginPatterns("*")//是否允许cookie.allowCredentials(true)//设置允许的请求方式.allowedMethods("GET", "POST", "DELETE", "PUT")//设置允许的header属性.allowedHeaders("*")//跨域允许时间.maxAge(3600);}
}

第二步:在SpringSecurity配置类的configre()方法中添加如下代码:

//允许跨域
http.cors();

【精品】SpringSecurity在前后端分离项目中的应用相关推荐

  1. SpringBoot前后端分离项目中如何制作前端jar包(类似swaggerUI前端jar包制作方法)

    SpringBoot前后端分离项目中如何制作前端jar包(类似swaggerUI前端jar包制作方法) 可用于SpringBoot引用的前端UI的Jar包,类似于SwaggerUI包 WABJAR介绍 ...

  2. 阿里云视频上传视频获取进度条问题(使用session方案,获取进度一直为0的解决方案)补充:前后端分离项目中获取进度解决方案

    1.场景描述: 之前用阿里云上传视频,前端反应上传视频经常出现获取视频url失败问题.但是接口我测过很多遍都是没有问题的.后台这边提供了一个视频上传的接口返回一个videoId,还提供了一个根据vid ...

  3. springBoot 解决前后端分离项目中跨越请求,同源策略

    今天在做项目的过程,采用前后端分离技术的时遇到采用ajax请求无法访问后台接口,按F12,查看浏览器运行状态时,报如下错误 为了解决浏览的同源策略,就必须了解什么是同源策略. 1.什么是同源策略 同源 ...

  4. pageoffice在vue+springboot前后端分离项目中的应用方法

    1.因浏览器禁用插件无法使用内嵌在浏览器内部的pageoffice打开方式,需要使用4.0新增的POBrowser方式来在外部打开一个窗口去在线打开office 故下面介绍的是基于4.0在线打开文档的 ...

  5. (五)Debian Linux中部署Spring Boot + Vue的前后端分离项目详细过程(arm64/aarch64架构下)

    专题系列往期文章目录 (一)移动端安卓手机改造成linux服务器&Linux中安装软件踩坑历险记 (二)Debian Linux系统中安装oracle JDK1.8详细过程(arm64/aar ...

  6. 前后端分离项目,后端是如何处理前端传递的token?

    前后端分离项目中,在不使用 SpringSecurity.Shiro 安全框架的情况下,后端是如何处理前段传递的 token 的呢? 简单说一个场景,在一个非常小的项目中,由于业务逻辑比较简单,也没有 ...

  7. 视频教程-Vue、Spring Boot开发小而完整的Web前后端分离项目实战-Java

    Vue.Spring Boot开发小而完整的Web前后端分离项目实战 3年多.net开发经验:5年的java后端开发经验,熟悉行.net,java流行技术,拥有多个.net,java web企业级应; ...

  8. 前后端分离项目,如何解决跨域问题?

    跨域问题是前后端分离项目中非常常见的一个问题,举例来说,编程猫(codingmore)学习网站的前端服务跑在 8080 端口下,后端服务跑在 9002 端口下,那么前端在请求后端接口的时候就会出现跨域 ...

  9. 前后端分离项目跨域问题及解决方案

    目录 1.什么是跨域 2.前后端分离项目中的跨域问题 3.方法一:SpringBoot后端进行处理 4.方法二:在Vue前端进行处理 5.总结 1.什么是跨域 请求同域资源: 在域名 (或 ip 地址 ...

最新文章

  1. 大数据产业不只是建设数据中心
  2. js --- 递归结构图
  3. matlab的支持向量机调参,支持向量机(2)-应用
  4. 《江湖X:汉家江湖》游戏论剑系统技术全解析
  5. 【ArcGIS风暴】实验:公路建设成本的计算
  6. 十大有用但又偏执的Java编程技术
  7. 电脑运行java游戏,电脑运行软件卡顿?这几招游戏或是办公,让你速度飞起!...
  8. jQuery插件素材网站
  9. 我爱计算机视觉干货集锦分类汇总(2019年3月9日)
  10. Java开发桌面程序学习(七)——ImageView设置图片以及jar包读取fxml文件
  11. 计算机视觉实战(八)直方图与傅里叶变换
  12. 如何爬取猫眼全部信息(电影信息、演员信息)
  13. 安卓12解除进程限制方法汇总
  14. 马哥linux作业,马哥linux0803作业内容
  15. 2021-09-28 网安实验-取证分析-Pcap流量包取证
  16. 终于稀里糊涂完成了模仿天猫整站ssm
  17. 适用于 Windows 的 iTunes 10.12.9.3 64位 最新版 下载,支持 iOS 14
  18. 数据库服务的运行与登录
  19. 建功立业的秘诀就是:立即行动!
  20. 科大讯飞麦克风阵列(6麦)上手体验

热门文章

  1. 力扣报错:runtime error: reference binding to null pointer of type ‘std::vector<int
  2. 计算机office办公知识,计算机基础知识和office办公系列软件的使用(完整版)
  3. 西游释厄传2游戏技巧
  4. 汽车电子产品热评估与散热措施
  5. 微信小程序的拖拽、缩放和旋转手势
  6. Cocos2d-x 3.0 开发(八)骨骼动画的动态换肤
  7. 类和对象的特性(C++谭浩强第三版笔记)
  8. 制作网站你需要了解的3个步骤
  9. 微博与中国版SNS的未来
  10. 2023年6月东莞/惠州/深圳CPDA数据分析师认证招生