承接前文Springboot security cas整合方案-原理篇,请在理解原理的情况下再查看实践篇

maven环境

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-security</artifactId>
  4. </dependency>
  5. <!-- 添加spring security cas支持 -->
  6. <dependency>
  7. <groupId>org.springframework.security</groupId>
  8. <artifactId>spring-security-cas</artifactId>
  9. </dependency>

cas基础配置

包含配置文件以及对应的VO类

  1. src/main/resources/application-cas.yml
  1. cas:
  2. server:
  3. host:
  4. url: http://192.168.1.101/cas #cas服务地址
  5. login_url: /login #登录地址
  6. logout_url: /logout #注销地址
  7. app:
  8. server:
  9. host:
  10. url: http://localhost:8080/web-cas #本应用访问地址
  11. login:
  12. url: /login/cas #本应用登录地址
  13. logout:
  14. url: /logout #本应用退出地址
  1. 对应的VO类,应用@Component注解加载
  1. @Component
  2. public class AcmCasProperties {
  3. @Value("${cas.server.host.url}")
  4. private String casServerPrefix;
  5. @Value("${cas.server.host.login_url}")
  6. private String casServerLoginUrl;
  7. @Value("${cas.server.host.logout_url}")
  8. private String casServerLogoutUrl;
  9. @Value("${app.server.host.url}")
  10. private String appServicePrefix;
  11. @Value("${app.login.url}")
  12. private String appServiceLoginUrl;
  13. @Value("${app.logout.url}")
  14. private String appServiceLogoutUrl;
  15. public String getCasServerPrefix() {
  16. return LocalIpUtil.replaceTrueIpIfLocalhost(casServerPrefix);
  17. }
  18. public void setCasServerPrefix(String casServerPrefix) {
  19. this.casServerPrefix = casServerPrefix;
  20. }
  21. public String getCasServerLoginUrl() {
  22. return casServerLoginUrl;
  23. }
  24. public void setCasServerLoginUrl(String casServerLoginUrl) {
  25. this.casServerLoginUrl = casServerLoginUrl;
  26. }
  27. public String getCasServerLogoutUrl() {
  28. return casServerLogoutUrl;
  29. }
  30. public void setCasServerLogoutUrl(String casServerLogoutUrl) {
  31. this.casServerLogoutUrl = casServerLogoutUrl;
  32. }
  33. public String getAppServicePrefix() {
  34. return LocalIpUtil.replaceTrueIpIfLocalhost(appServicePrefix);
  35. }
  36. public void setAppServicePrefix(String appServicePrefix) {
  37. this.appServicePrefix = appServicePrefix;
  38. }
  39. public String getAppServiceLoginUrl() {
  40. return appServiceLoginUrl;
  41. }
  42. public void setAppServiceLoginUrl(String appServiceLoginUrl) {
  43. this.appServiceLoginUrl = appServiceLoginUrl;
  44. }
  45. public String getAppServiceLogoutUrl() {
  46. return appServiceLogoutUrl;
  47. }
  48. public void setAppServiceLogoutUrl(String appServiceLogoutUrl) {
  49. this.appServiceLogoutUrl = appServiceLogoutUrl;
  50. }
  51. }
  1. 其中用到了LocalIpUtil工具类,主要是替换localhost或者域名为真实的ip
  1. public class LocalIpUtil
  2. {
  3. private static Logger logger = LoggerFactory.getLogger(LocalIpUtil.class);
  4. private static final String WINDOWS = "WINDOWS";
  5. public static void main(String[] args)
  6. {
  7. String url = "http://127.0.0.1:8080/client1";
  8. System.out.println(replaceTrueIpIfLocalhost(url));
  9. }
  10. public static String replaceTrueIpIfLocalhost(String url) {
  11. String localIp = getLocalIp();
  12. if ((url.contains("localhost")) || (url.contains("127.0.0.1"))) {
  13. url = url.replaceAll("localhost", localIp).replaceAll("127.0.0.1", localIp);
  14. }
  15. return url;
  16. }
  17. private static String getLocalIp()
  18. {
  19. String os = System.getProperty("os.name").toUpperCase();
  20. String address = "";
  21. if (os.contains("WINDOWS"))
  22. try {
  23. address = InetAddress.getLocalHost().getHostAddress();
  24. } catch (UnknownHostException e) {
  25. logger.error("windows获取本地IP出错", e);
  26. }
  27. else {
  28. address = getLinuxIP();
  29. }
  30. return address;
  31. }
  32. private static String getLinuxIP()
  33. {
  34. String address = "";
  35. try
  36. {
  37. Enumeration allNetInterfaces = NetworkInterface.getNetworkInterfaces();
  38. InetAddress ip = null;
  39. while (allNetInterfaces.hasMoreElements()) {
  40. NetworkInterface netInterface = (NetworkInterface)allNetInterfaces.nextElement();
  41. if ((netInterface.isUp()) && (!netInterface.isLoopback()) && (!netInterface.isVirtual()))
  42. {
  43. Enumeration addresses = netInterface.getInetAddresses();
  44. while (addresses.hasMoreElements()) {
  45. ip = (InetAddress)addresses.nextElement();
  46. if ((!ip.isLoopbackAddress()) &&
  47. (ip != null) && ((ip instanceof Inet4Address)))
  48. address = ip.getHostAddress();
  49. }
  50. }
  51. }
  52. } catch (SocketException e) {
  53. logger.error("linux获取本地IP出错", e);
  54. }
  55. return address;
  56. }

Springboot 应用cas配置

src/main/resources/application.yml应用application-cas.yml

  1. spring:
  2. profiles:
  3. active: cas

Springboot 配置cas过滤链

这里采用@Configuration@Bean注解来完成,包括LogoutFilterSingleSignOutFilterticket校验器service配置对象cas凭证校验器ProviderCasAuthenticationEntryPoint-cas认证入口

  1. @Configuration
  2. public class AcmCasConfiguration {
  3. @Resource
  4. private AcmCasProperties acmCasProperties;
  5. /**
  6. * 设置客户端service的属性
  7. * <p>
  8. * 主要设置请求cas服务端后的回调路径,一般为主页地址,不可为登录地址
  9. *
  10. * </p>
  11. *
  12. * @return
  13. */
  14. @Bean
  15. public ServiceProperties serviceProperties() {
  16. ServiceProperties serviceProperties = new ServiceProperties();
  17. // 设置回调的service路径,此为主页路径
  18. serviceProperties.setService(acmCasProperties.getAppServicePrefix() + "/index.html");
  19. // 对所有的未拥有ticket的访问均需要验证
  20. serviceProperties.setAuthenticateAllArtifacts(true);
  21. return serviceProperties;
  22. }
  23. /**
  24. * 配置ticket校验器
  25. *
  26. * @return
  27. */
  28. @Bean
  29. public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {
  30. // 配置上服务端的校验ticket地址
  31. return new Cas20ServiceTicketValidator(acmCasProperties.getCasServerPrefix());
  32. }
  33. /**
  34. * 单点注销,接受cas服务端发出的注销session请求
  35. *
  36. * @see SingleLogout(SLO) Front or Back Channel
  37. *
  38. * @return
  39. */
  40. @Bean
  41. public SingleSignOutFilter singleSignOutFilter() {
  42. SingleSignOutFilter outFilter = new SingleSignOutFilter();
  43. // 设置cas服务端路径前缀,应用于front channel的注销请求
  44. outFilter.setCasServerUrlPrefix(acmCasProperties.getCasServerPrefix());
  45. outFilter.setIgnoreInitConfiguration(true);
  46. return outFilter;
  47. }
  48. /**
  49. * 单点请求cas客户端退出Filter类
  50. *
  51. * 请求/logout,转发至cas服务端进行注销
  52. */
  53. @Bean
  54. public LogoutFilter logoutFilter() {
  55. // 设置回调地址,以免注销后页面不再跳转
  56. StringBuilder logoutRedirectPath = new StringBuilder();
  57. logoutRedirectPath.append(acmCasProperties.getCasServerPrefix())
  58. .append(acmCasProperties.getCasServerLogoutUrl()).append("?service=")
  59. .append(acmCasProperties.getAppServicePrefix());
  60. LogoutFilter logoutFilter = new LogoutFilter(logoutRedirectPath.toString(), new SecurityContextLogoutHandler());
  61. logoutFilter.setFilterProcessesUrl(acmCasProperties.getAppServiceLogoutUrl());
  62. return logoutFilter;
  63. }
  64. /**
  65. * 创建cas校验类
  66. *
  67. * <p>
  68. * <b>Notes:</b> TicketValidator、AuthenticationUserDetailService属性必须设置;
  69. * serviceProperties属性主要应用于ticketValidator用于去cas服务端检验ticket
  70. * </p>
  71. *
  72. * @return
  73. */
  74. @Bean("casProvider")
  75. public CasAuthenticationProvider casAuthenticationProvider(
  76. AuthenticationUserDetailsService<CasAssertionAuthenticationToken> userDetailsService) {
  77. CasAuthenticationProvider provider = new CasAuthenticationProvider();
  78. provider.setKey("casProvider");
  79. provider.setServiceProperties(serviceProperties());
  80. provider.setTicketValidator(cas20ServiceTicketValidator());
  81. provider.setAuthenticationUserDetailsService(userDetailsService);
  82. return provider;
  83. }
  84. /**
  85. * ==============================================================
  86. * ==============================================================
  87. */
  88. /**
  89. * 认证的入口,即跳转至服务端的cas地址
  90. *
  91. * <p>
  92. * <b>Note:</b>浏览器访问不可直接填客户端的login请求,若如此则会返回Error页面,无法被此入口拦截
  93. * </p>
  94. */
  95. @Bean
  96. public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
  97. CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint();
  98. entryPoint.setServiceProperties(serviceProperties());
  99. entryPoint.setLoginUrl(acmCasProperties.getCasServerPrefix() + acmCasProperties.getCasServerLoginUrl());
  100. return entryPoint;
  101. }
  102. }

下面对上述的AuthenticationUserDetailsService需要手动配置下,用于权限集合的获取

配置cas获取权限集合的AuthenticationUserDetailsService

  1. @Component
  2. public class AcmCasUserDetailService implements AuthenticationUserDetailsService<CasAssertionAuthenticationToken> {
  3. private static final Logger USER_SERVICE_LOGGER = LoggerFactory.getLogger(AcmCasUserDetailService.class);
  4. @Resource
  5. private TSysUserDao tsysUserDAO;
  6. @Override
  7. public UserDetails loadUserDetails(CasAssertionAuthenticationToken token) throws UsernameNotFoundException {
  8. USER_SERVICE_LOGGER.info("校验成功的登录名为: " + token.getName());
  9. //此处涉及到数据库操作然后读取权限集合,读者可自行实现
  10. SysUser sysUser = tsysUserDAO.findByUserName(token.getName());
  11. if (null == sysUser) {
  12. throw new UsernameNotFoundException("username isn't exsited in log-cms");
  13. }
  14. return sysUser;
  15. }
  16. }

示例中的SysUser实现了UserDetail接口,实现的方法代码如下

  1. @Override
  2. public Collection<? extends GrantedAuthority> getAuthorities() {
  3. List<GrantedAuthority> auths = new ArrayList<>();
  4. //获取用户对应的角色集合
  5. List<SysRole> roles = this.getSysRoles();
  6. for (SysRole role : roles) {
  7. //手动加上ROLE_前缀
  8. auths.add(new SimpleGrantedAuthority(SercurityConstants.prefix+role.getRoleName()));
  9. }
  10. return auths;
  11. }

FilterSecurityInterceptor配置

需要配置权限的认证过滤链

  1. @Component
  2. public class CasFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
  3. @Resource
  4. private FilterInvocationSecurityMetadataSource securityMetadataSource;
  5. @Resource
  6. public void setMyAccessDecisionManager(AccessDecisionManager myAccessDecisionManager) {
  7. super.setAccessDecisionManager(myAccessDecisionManager);
  8. }
  9. @Override
  10. public void init(FilterConfig filterConfig) throws ServletException {
  11. }
  12. @Override
  13. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  14. FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
  15. invoke(fi);
  16. }
  17. private void invoke(FilterInvocation fi) throws IOException, ServletException {
  18. //fi里面有一个被拦截的url
  19. //里面调用CasInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限
  20. //再调用CasAccessDecisionManager的decide方法来校验用户的权限是否足够
  21. InterceptorStatusToken token = super.beforeInvocation(fi);
  22. try {
  23. //执行下一个拦截器
  24. fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
  25. } finally {
  26. super.afterInvocation(token, null);
  27. }
  28. }
  29. @Override
  30. public void destroy() {
  31. }
  32. @Override
  33. public Class<?> getSecureObjectClass() {
  34. return FilterInvocation.class;
  35. }
  36. @Override
  37. public SecurityMetadataSource obtainSecurityMetadataSource() {
  38. return this.securityMetadataSource;
  39. }
  40. }

其中还涉及到SecurityMetadataSource-当前访问路径的权限获取AccessDecisionManager-授权处理器

SecurityMetadataSource-当前访问路径的权限获取

  1. @Component
  2. public class CasInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource {
  3. private final TSysMenuDao tSysMenuDao;
  4. private final HashSet<Pattern> patterns;
  5. private final Logger logger = LoggerFactory.getLogger(this.getClass());
  6. @Autowired
  7. public MyInvocationSecurityMetadataSourceService(TSysMenuDao tSysMenuDao,FilterStatic filterStatic) {
  8. this.tSysMenuDao = tSysMenuDao;
  9. patterns = new HashSet<>();
  10. //可通过配置过滤路径,这里就省略不写了,写法与AcmCasProperties一致
  11. for (String filter:filterStatic.getStaticFilters()){
  12. String regex= filter.replace("**","*").replace("*",".*");
  13. patterns.add(Pattern.compile(regex));
  14. }
  15. }
  16. /**
  17. * 查找url对应的角色
  18. */
  19. public Collection<ConfigAttribute> loadResourceDefine(String url){
  20. Collection<ConfigAttribute> array=new ArrayList<>();
  21. ConfigAttribute cfg;
  22. SysMenu permission = tSysMenuDao.findMeneRoles(url);
  23. if (permission !=null) {
  24. for (String role :permission.getRoles().split(",")){
  25. cfg = new SecurityConfig(role);
  26. //此处只添加了用户的名字,其实还可以添加更多权限的信息,例如请求方法到ConfigAttribute的集合中去。此处添加的信息将会作为CasAccessDecisionManager类的decide的第三个参数。
  27. array.add(cfg);
  28. }
  29. return array;
  30. }
  31. return null;
  32. }
  33. @Override
  34. public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
  35. //object 中包含用户请求的request 信息
  36. HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
  37. String url = request.getRequestURI();
  38. url = url.replaceFirst(request.getContextPath(), "");
  39. logger.info(url);
  40. //将请求的url与配置文件中不需要访问控制的url进行匹配
  41. Iterator<Pattern> patternIterator=patterns.iterator();
  42. while (patternIterator.hasNext()){
  43. Pattern pattern = patternIterator.next();
  44. Matcher matcher=pattern.matcher(url);
  45. if (matcher.find())
  46. return null;
  47. }
  48. return loadResourceDefine(url);
  49. }
  50. @Override
  51. public Collection<ConfigAttribute> getAllConfigAttributes() {
  52. return null;
  53. }
  54. @Override
  55. public boolean supports(Class<?> aClass) {
  56. return true;
  57. }
  58. }

AccessDecisionManager-授权处理器

承接上面的SecurityMetadataSource获取到的权限集合configAttributes,此处对此验证

  1. @Component
  2. public class CasAccessDecisionManager implements AccessDecisionManager {
  3. /**
  4. * @param authentication 当前用户权限信息
  5. * @param o 请求信息
  6. * @param configAttributes 当前访问的url对应的角色
  7. */
  8. @Override
  9. public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
  10. //没有角色要求则返回
  11. if(null== configAttributes || configAttributes.size() <=0) {
  12. return;
  13. }
  14. //比较当前用户角色和当前访问的url对应的角色,是否拥有对应权限
  15. ConfigAttribute c;
  16. String needRole;
  17. for(Iterator<ConfigAttribute> iter = configAttributes.iterator(); iter.hasNext(); ) {
  18. c = iter.next();
  19. needRole = c.getAttribute();
  20. for(GrantedAuthority ga : authentication.getAuthorities()) {//authentication 为在注释1 中循环添加到 GrantedAuthority 对象中的权限信息集合
  21. if((SercurityConstants.prefix+needRole.trim()).equals(ga.getAuthority())) {
  22. return;
  23. }
  24. }
  25. }
  26. throw new AccessDeniedException("no right");
  27. }
  28. @Override
  29. public boolean supports(ConfigAttribute configAttribute) {
  30. return true;
  31. }
  32. @Override
  33. public boolean supports(Class<?> aClass) {
  34. return true;
  35. }
  36. }

总入口配置

主要是结合spring security进行相应的设置,因为CasAuthenticationFilter需要设置AuthenticationManager对象,所以放在总入口这里配置

  1. @Configuration
  2. @EnableWebSecurity
  3. //如果依赖数据库读取角色等,则需要配置
  4. @AutoConfigureAfter(MyBatisMapperScannerConfig.class)
  5. public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  6. /**
  7. * 自定义动态权限过滤器
  8. */
  9. @Resource
  10. private final CasFilterSecurityInterceptor myFilterSecurityInterceptor;
  11. @Resource
  12. private final FilterStatic filterStatic;
  13. /**
  14. * 自定义过滤规则及其安全配置
  15. */
  16. @Override
  17. protected void configure(HttpSecurity http) throws Exception {
  18. // HeadersConfigurer
  19. http.headers().frameOptions().disable();
  20. // CsrfConfigurer
  21. http.csrf().disable();
  22. // ExpressionInterceptUrlRegistry
  23. http.authorizeRequests().anyRequest().authenticated().anyRequest().fullyAuthenticated();
  24. // acm cas策略
  25. // 对logout请求放行
  26. http.logout().permitAll();
  27. // 入口
  28. CasAuthenticationEntryPoint entryPoint = getApplicationContext().getBean(CasAuthenticationEntryPoint.class);
  29. CasAuthenticationFilter casAuthenticationFilter = getApplicationContext()
  30. .getBean(CasAuthenticationFilter.class);
  31. SingleSignOutFilter singleSignOutFilter = getApplicationContext().getBean(SingleSignOutFilter.class);
  32. LogoutFilter logoutFilter = getApplicationContext().getBean(LogoutFilter.class);
  33. /**
  34. * 执行顺序为
  35. * LogoutFilter-->SingleSignOutFilter-->CasAuthenticationFilter-->
  36. * ExceptionTranslationFilter
  37. */
  38. http.exceptionHandling().authenticationEntryPoint(entryPoint).and().addFilter(casAuthenticationFilter)
  39. .addFilterBefore(logoutFilter, LogoutFilter.class)
  40. .addFilterBefore(singleSignOutFilter, CasAuthenticationFilter.class);
  41. }
  42. // addFilter
  43. http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class);
  44. }
  45. @Autowired
  46. public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
  47. //放入cas凭证校验器
  48. AuthenticationProvider authenticationProvider = (AuthenticationProvider) getApplicationContext()
  49. .getBean("casProvider");
  50. auth.authenticationProvider(authenticationProvider);
  51. }
  52. @Override
  53. public void configure(WebSecurity web) throws Exception {
  54. // 静态文静过滤
  55. String[] filter = filterStatic.getStaticFilters().toArray(new String[0]);
  56. web.ignoring().antMatchers(filter);
  57. }
  58. /**
  59. * cas filter类
  60. *
  61. * 针对/login请求的校验
  62. *
  63. * @return
  64. */
  65. @Bean
  66. public CasAuthenticationFilter casAuthenticationFilter(ServiceProperties properties,
  67. AcmCasProperties acmCasProperties) throws Exception {
  68. CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
  69. casAuthenticationFilter.setServiceProperties(properties);
  70. casAuthenticationFilter.setFilterProcessesUrl(acmCasProperties.getAppServiceLoginUrl());
  71. casAuthenticationFilter.setAuthenticationManager(authenticationManager());
  72. casAuthenticationFilter
  73. .setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler("/index.html"));
  74. return casAuthenticationFilter;
  75. }
  76. }

Springboot启动类配置

  1. @SpringBootApplication
  2. @ComponentScan(basePackages = {"com.jingsir.springboot.cas"})
  3. public class Application extends SpringBootServletInitializer implements EmbeddedServletContainerCustomizer {
  4. public static void main(String[] args) {
  5. SpringApplication.run(Application.class);
  6. }
  7. @Override
  8. public void customize(ConfigurableEmbeddedServletContainer configurableEmbeddedServletContainer) {
  9. configurableEmbeddedServletContainer.setContextPath("/cas-web");
  10. }
  11. }

小结

当时对CasAuthenticationEntryPoint为何配置的service回调路径不可为本应用的login登录路径有疑惑,因为会被提前拦截显示"401错误"。分析wireshark的抓包后得知结论如下

  • 第一次用户GET请求到casServerLoginUrl,返回登录页面
  • 用户输入账号与密码后POST请求到casServerLoginUrl,其会返回TGC,并不返回ticket(所以此处不可为本应用的登录路径),由于FilterSecurityInterceptor校验仍失败,则仍会由ExceptionTranslationFilter发送GET请求转发至cas登录页面
  • 第二次用户GET请求到casServerLoginUrl,cas服务根据TGC会返回Ticket
  • 客户端拿到Ticket后会路由至cas服务上的/cas/serviceValidate上进行Ticket校验,校验通过后则访问真正的路径。且后面每次的请求都会携带Ticket去cas服务上校验,直至Ticket失效后则再次进行登录

本文都是通过实例操作后所写的博客,建议理解原理之后再可参照实例来编写,不当之处欢迎指出。

Springboot security cas整合方案-实践篇的更多相关文章

  1. Springboot security cas整合方案-原理篇

    前言:网络中关于Spring security整合cas的方案有很多例,对于Springboot security整合cas方案则比较少,且有些仿制下来运行也有些错误,所以博主在此篇详细的分析cas原 ...

  2. Springboot security cas源码陶冶-CasAuthenticationFilter

    Springboot security cas整合方案中不可或缺的校验Filter类或者称为认证Filter类,其内部包含校验器.权限获取等,特开辟新地啃啃 继承结构 - AbstractAuthen ...

  3. Springboot security cas源码陶冶-ExceptionTranslationFilter

    拦截关键的两个异常,对异常进行处理.主要应用异常则跳转至cas服务端登录页面 ExceptionTranslationFilter#doFilter-逻辑入口 具体操作逻辑如下 public void ...

  4. Springboot security cas源码陶冶-FilterSecurityInterceptor

    前言:用户登录信息校验成功后,都会获得当前用户所拥有的全部权限,所以对访问的路径当前用户有无权限则需要拦截验证一发 Spring security过滤器的执行顺序 首先我们需要验证为啥FilterSe ...

  5. 【手摸手,带你搭建前后端分离商城系统】03 整合Spring Security token 实现方案,完成主业务登录

    [手摸手,带你搭建前后端分离商城系统]03 整合Spring Security token 实现方案,完成主业务登录 上节里面,我们已经将基本的前端 VUE + Element UI 整合到了一起.并 ...

  6. Spring Security 3整合CAS 实现SSO

    spring security 3整合cas client用于实现各Application之间的单点登录. 1. 需要准备的jar spring-security-core-3.0.8.RELEASE ...

  7. CAS Spring Security 3 整合配置(转)

    一般来说, Web 应用的安全性包括用户认证( Authentication )和用户授权( Authorization )两个部分.用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否 ...

  8. springboot+security整合(3)自定义鉴权

    说明 springboot 版本 2.0.3源码地址:点击跳转 系列 springboot+security 整合(1) springboot+security 整合(2) springboot+se ...

  9. springboot+security整合(2)自定义校验

    说明 springboot 版本 2.0.3源码地址:点击跳转 系列 springboot+security 整合(1) springboot+security 整合(2) springboot+se ...

随机推荐

  1. C/C++中peek函数的原理及应用

    C++中的peek函数 该调用形式为cin.peek() 其返回值是一个char型的字符,其返回值是指针指向的当前字符,但它只是观测,指针仍停留在当前位置,并不后移.如果要访问的字符是文件结束符,则函 ...

  2. POJ 2209 The King(简单贪心)

    The King Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 7499   Accepted: 4060 Descript ...

  3. HDU 2289 Cup【高精度,二分】

    Cup Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...

  4. BZOJ 1257: [CQOI2007]余数之和sum【神奇的做法,思维题】

    1257: [CQOI2007]余数之和sum Time Limit: 5 Sec  Memory Limit: 162 MBSubmit: 4474  Solved: 2083[Submit][St ...

  5. hdu_1018(斯大林公式/n!的位数)

    题意:求大数n!的位数. 根据n! = (int)log(n!)+1 方法1: log(n!) = log(1*2*3*...*n) = log1+log2+...+logn 方法2: 斯大林公式: ...

  6. Node类型知识大全

    Node类型 1.节点关系 每个节点都有一个childNodes属性,其中保存着一个NodeList对象.NodeList是一种类数组对象,用于保存一组有序的节点,可以通过位置来访问这些节点.请注意, ...

  7. str_repeat() 函数把字符串重复指定的次数。

    str_repeat() 函数把字符串重复指定的次数. str_repeat(string,repeat) 参数 描述 string 必需.规定要重复的字符串. repeat 必需.规定字符串将被重复 ...

  8. php通过ini_set调用output_compression压缩网页

    网页压缩是一种网页优化技术,可以让网页体积缩小后再传输到客户端,从而减少数据传送量,提高速度.这种技术现在使用已经相当普遍,绝大多数网页都使用了这种技术. 网页压缩可以在服务器或空间里通过参数设置启用 ...

  9. dede文章插入分页符不起作用,编辑器中出现分页符,导致文章显示不全

    文章来源:小灰博客| 时间:2013-10-30 13:40:21| 作者:Leo | 1 条评论 文章分类:IT技术分享.PHP     标签: dedecms 今天偶尔发现给一篇dede下的长文章 ...

  10. 织梦CMS搭建网站必做的服务器相关安全设置

    http://help.dedecms.com/install-use/server/2011/1109/2124.html#printSource http://www.aliweihu.com/9 ...