怎么理解Spring Boot认证和鉴权
本篇内容介绍了“怎么理解Spring Boot认证和鉴权”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
在web应用中有大量场景需要对用户进行安全校,一般人的做法就是硬编码的方式直接埋到到业务代码中,但可曾想过这样做法会导致代码不够简洁(大量重复代码)、有个性化时难维护(每个业务逻辑访问控制策略都不相同甚至差异很大)、容易发生安全泄露(有些业务可能不需要当前登录信息,但被访问的数据可能是敏感数据由于遗忘而没有受到保护)。
为了更安全、更方便的进行访问安全控制,我们可以想到的就是使用springmvc的拦截器(HandlerInterceptor),但其实更推荐使用更为成熟的spring security来完成认证和鉴权。
拦截器
拦截器HandlerInterceptor确实可以帮我们完成登录拦截、或是权限校验、或是防重复提交等需求。其实基于它也可以实现基于url或方法级的安全控制。
如果你对spring mvc的请求处理流程相对的了解,它的原理容易理解,具体可以参阅我之前的分享。
public interface HandlerInterceptor {boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception; void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;}//你可以基于有些url进行拦截@Configurationpublic class UserSecurityInterceptor extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { String[] securityUrls = new String[]{"esbdictionary @Bean public HttpFirewall allowUrlEncodedSlashHttpFirewall() { DefaultHttpFirewall firewall = new DefaultHttpFirewall(); firewall.setAllowUrlEncodedSlash(true); return firewall; } @Bean public AuthInterceptor userLoginInterceptor() { return new AuthInterceptor(); } public class AuthInterceptor implements HandlerInterceptor { public Logger logger = LoggerFactory.getLogger(AuthInterceptor.class); @Autowired private ApplicationContext applicationContext; public AuthInterceptor() { } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { LoginUserInfo user = null; try { user = (LoginUserInfo) SSOUserUtils.getCurrentLoginUser(); } catch (Exception e) { logger.error("从SSO登录信息中获取用户信息失败! 详细错误信息:%s", e); throw new ServletException("从SSO登录信息中获取用户信息失败!", e); } String[] profiles = applicationContext.getEnvironment().getActiveProfiles(); if (!Arrays.isNullOrEmpty(profiles)) { if ("dev".equals(profiles[0])) { return true; } } if (user == null || UserUtils.ANONYMOUS_ROLE_ID.equals(user.getRoleId())) { throw new ServletException("获取登录用户信息失败!"); } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }}
认证
确认一个访问请求发起的时候背后的用户是谁,他的用户信息是怎样的。在spring security 里面认证支持很多种方式,最简单的就是用户名密码,还有LDAP、OpenID、CAS等等。
而在我们的系统里面,用户信息需要通过kxtx-sso模块进行获取。通过sso认证比较简单,就是要确认用户是否通过会员系统登录,并把登录信息包装成授权对象放到SecurityContext中,通过一个filter来完成:
@Data@EqualsAndHashCode(callSuper = false)public class SsoAuthentication extends AbstractAuthenticationToken { private static final long serialVersionUID = -1799455508626725119L; private LoginUserInfo user; public SsoAuthentication(LoginUserInfo user) { super(null); this.user = user; } @Override public Object getCredentials() { return "kxsso"; } @Override public Object getPrincipal() { return user; } @Override public String getName() { return user.getName(); }}public class SsoAuthenticationProcessingFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { LoginUserInfo user = (LoginUserInfo) SSOUserUtils.getCurrentLoginUser(); SsoAuthentication auth = new SsoAuthentication(user ); SecurityContextHolder.getContext().setAuthentication(auth); filterChain.doFilter(request, response); }}@Componentpublic class SsoAuthenticationProvider implements AuthenticationProvider { @Value("${env}") String env; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { LoginUserInfo loginUserInfo = (LoginUserInfo) authentication.getPrincipal(); if (!UserUtils.ANONYMOUS_ROLE_ID.equals(loginUserInfo.getRoleId()) || "dev".equals(env)) { authentication.setAuthenticated(true); } else { throw new BadCredentialsException("请登录"); } return authentication; } @Override public boolean supports(Class<?> authentication) { return SsoAuthentication.class.equals(authentication); }}@Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)public class WebSecurityConfig extends WebSecurityConfigurerAdapter { protected void configure(HttpSecurity http) throws Exception { // 关闭session http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and(); // 允许访问所有URL,通过方法保护的形式来限制访问。 http.authorizeRequests().anyRequest().permitAll(); // 注册sso filter http.addFilterBefore(ssoAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class); } @Bean SsoAuthenticationProcessingFilter ssoAuthenticationProcessingFilter() { return new SsoAuthenticationProcessingFilter(); }}
鉴权
控制一个功能是否能被当前用户访问,对不符合要求的用户予以拒绝。spring security 主要有两种控制点:
基于请求路径的:控制某一URL模式必须符合某种要求;
基于方法的:控制某一方法必须符合某种要求;
而控制形式就比较多样化了:
代码配置;
xml配置;
注解控制;
el表达式;
自定义访问控制器;
目前鉴权的需求比较简单:登录允许访问,未登录禁止访问。因此可以定义了一个切面,控制所有需要安全控制的Controller。
spring security 提供了一些注解:
@PreAuthorize | 控制一个方法是否能够被调用,业务方法(HandlerMethod )的前置处理,比如: @PreAuthorize("#id<10")限制只能查询Id小于10的用户 @PreAuthorize("principal.username.equals(#username)")限制只能查询自己的信息 @PreAuthorize("#user.name.equals('abc')")限制只能新增用户名称为abc的用户 |
@PostAuthorize | 业务方法调用完之后进行权限检查,后置处理,比如: @PostAuthorize("returnObject.id%2==0") public User find(int id) {} 返回值的id是偶数则表示校验通过,否则表示校验失败,将抛出AccessDeniedException |
@PreFilter | 对集合类型的参数进行过滤,比如: 对集合ids中id不为偶数的进行移除 @PreFilter(filterTarget="ids", value="filterObject%2==0") public void delete(List<Integer> ids, List<String> usernames) {} |
@PostFilter | 对集合类型的返回值进行过滤,比如: 将对返回结果中id不为偶数的list中的对象进行移除 @PostFilter("filterObject.id%2==0") public List<User> findAll() {} |
@AuthenticationPrincipal | 解决在业务方法内对当前用户信息的方法 |
@Aspect@Componentpublic class InControllerAspect { @Autowired BeforeInControllerMethods beforeInMethods; @Pointcut("execution(public * com.kxtx.oms.portal.controller.in.*.*(..)) && @annotation(org.springframework.web.bind.annotation.RequestMapping)") public void hp() { }; @Before("hp()") public void befor() { beforeInMethods.before(); }}@Componentpublic class BeforeInControllerMethods { //@PreAuthorize("authenticated")要求所有访问此方法的用户必须登录 @PreAuthorize("authenticated") public void before() { }}//用户信息获取@RequestMapping("/order/submit")public ModelAndView findMessagesForUser(@AuthenticationPrincipal CustomUser customUser) { // .. find messages for this user and return them ...}
是不是有点复杂,复杂的是表现形式,实际上需要真正理解它的目的(为了要解决什么问题)。
“怎么理解Spring Boot认证和鉴权”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341