在线演示

演示地址:http://139.196.87.48:9002/kitty

用户名:admin 密码:admin

技术背景

到目前为止,我们使用的权限认证框架是 Shiro,虽然 Shiro 也足够好用并且简单,但对于 Spring 官方主推的安全框架 Spring Security,用户群也是甚大的,所以我们这里把当前的代码切分出一个 shiro-cloud 分支,作为 Shiro + Spring Cloud 技术的分支代码,dev 和 master 分支将替换为 Spring Security + Spring Cloud 的技术栈,并在后续计划中集成 Spring Security OAuth2 实现单点登录功能。

代码实现

Maven依赖

移除shiro依赖,添加Spring Scurity和JWT依赖包,jwt目前的最新版本是0.9.1。

  1. <!-- spring security -->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-security</artifactId>
  5. </dependency>
  6. <!-- jwt -->
  7. <dependency>
  8. <groupId>io.jsonwebtoken</groupId>
  9. <artifactId>jjwt</artifactId>
  10. <version>${jwt.version}</version>
  11. </dependency>

权限注解

替换Shiro的权限注解为Spring Security的权限注解。

格式如下:

@PreAuthorize("hasAuthority('sys:menu:view')")

SysMenuController.java

  1. package com.louis.kitty.admin.controller;
  2.  
  3. import java.util.List;
  4.  
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.security.access.prepost.PreAuthorize;
  7. import org.springframework.web.bind.annotation.GetMapping;
  8. import org.springframework.web.bind.annotation.PostMapping;
  9. import org.springframework.web.bind.annotation.RequestBody;
  10. import org.springframework.web.bind.annotation.RequestMapping;
  11. import org.springframework.web.bind.annotation.RequestParam;
  12. import org.springframework.web.bind.annotation.RestController;
  13.  
  14. import com.louis.kitty.admin.model.SysMenu;
  15. import com.louis.kitty.admin.sevice.SysMenuService;
  16. import com.louis.kitty.core.http.HttpResult;
  17.  
  18. /**
  19. * 菜单控制器
  20. * @author Louis
  21. * @date Oct 29, 2018
  22. */
  23. @RestController
  24. @RequestMapping("menu")
  25. public class SysMenuController {
  26.  
  27. @Autowired
  28. private SysMenuService sysMenuService;
  29.  
  30. @PreAuthorize("hasAuthority('sys:menu:add') AND hasAuthority('sys:menu:edit')")
  31. @PostMapping(value="/save")
  32. public HttpResult save(@RequestBody SysMenu record) {
  33. return HttpResult.ok(sysMenuService.save(record));
  34. }
  35.  
  36. @PreAuthorize("hasAuthority('sys:menu:delete')")
  37. @PostMapping(value="/delete")
  38. public HttpResult delete(@RequestBody List<SysMenu> records) {
  39. return HttpResult.ok(sysMenuService.delete(records));
  40. }
  41.  
  42. @PreAuthorize("hasAuthority('sys:menu:view')")
  43. @GetMapping(value="/findNavTree")
  44. public HttpResult findNavTree(@RequestParam String userName) {
  45. return HttpResult.ok(sysMenuService.findTree(userName, ));
  46. }
  47.  
  48. @PreAuthorize("hasAuthority('sys:menu:view')")
  49. @GetMapping(value="/findMenuTree")
  50. public HttpResult findMenuTree() {
  51. return HttpResult.ok(sysMenuService.findTree(null, ));
  52. }
  53. }

Spring Security注解默认是关闭的,可以通过在配置类添加以下注解开启。

  1. @EnableGlobalMethodSecurity(prePostEnabled = true)

安全配置

添加安全配置类, 继承 WebSecurityConfigurerAdapter,配置URL验证策略和相关过滤器以及自定义的登录验证组件。

WebSecurityConfig.java

  1. package com.louis.kitty.admin.config;
  2.  
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.context.annotation.Configuration;
  6. import org.springframework.http.HttpMethod;
  7. import org.springframework.security.authentication.AuthenticationManager;
  8. import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
  9. import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
  10. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  11. import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
  12. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  13. import org.springframework.security.core.userdetails.UserDetailsService;
  14. import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
  15. import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
  16.  
  17. import com.louis.kitty.admin.security.JwtAuthenticationFilter;
  18. import com.louis.kitty.admin.security.JwtAuthenticationProvider;
  19.  
  20. /**
  21. * Spring Security Config
  22. * @author Louis
  23. * @date Nov 20, 2018
  24. */
  25. @Configuration
  26. @EnableWebSecurity
  27. @EnableGlobalMethodSecurity(prePostEnabled = true)
  28. public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  29.  
  30. @Autowired
  31. private UserDetailsService userDetailsService;
  32.  
  33. @Override
  34. public void configure(AuthenticationManagerBuilder auth) throws Exception {
  35. // 使用自定义身份验证组件
  36. auth.authenticationProvider(new JwtAuthenticationProvider(userDetailsService));
  37. }
  38.  
  39. @Override
  40. protected void configure(HttpSecurity http) throws Exception {
  41. // 禁用 csrf, 由于使用的是JWT,我们这里不需要csrf
  42. http.cors().and().csrf().disable()
  43. .authorizeRequests()
  44. // 跨域预检请求
  45. .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
  46. // web jars
  47. .antMatchers("/webjars/**").permitAll()
  48. // 查看SQL监控(druid)
  49. .antMatchers("/druid/**").permitAll()
  50. // 首页和登录页面
  51. .antMatchers("/").permitAll()
  52. .antMatchers("/login").permitAll()
  53. // swagger
  54. .antMatchers("/swagger-ui.html").permitAll()
  55. .antMatchers("/swagger-resources").permitAll()
  56. .antMatchers("/v2/api-docs").permitAll()
  57. .antMatchers("/webjars/springfox-swagger-ui/**").permitAll()
  58. // 验证码
  59. .antMatchers("/captcha.jpg**").permitAll()
  60. // 服务监控
  61. .antMatchers("/actuator/**").permitAll()
  62. // 其他所有请求需要身份认证
  63. .anyRequest().authenticated();
  64. // 退出登录处理器
  65. http.logout().logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler());
  66. // 登录认证过滤器
  67. http.addFilterBefore(new JwtAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class);
  68. }
  69.  
  70. @Bean
  71. @Override
  72. public AuthenticationManager authenticationManager() throws Exception {
  73. return super.authenticationManager();
  74. }
  75.  
  76. }

登录验证组件

继承 DaoAuthenticationProvider, 实现自定义的登录验证组件,覆写密码验证逻辑。

JwtAuthenticationProvider.java

  1. package com.louis.kitty.admin.security;
  2.  
  3. import org.springframework.security.authentication.BadCredentialsException;
  4. import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  5. import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
  6. import org.springframework.security.core.AuthenticationException;
  7. import org.springframework.security.core.userdetails.UserDetails;
  8. import org.springframework.security.core.userdetails.UserDetailsService;
  9.  
  10. import com.louis.kitty.admin.util.PasswordEncoder;
  11.  
  12. /**
  13. * 身份验证提供者
  14. * @author Louis
  15. * @date Nov 20, 2018
  16. */
  17. public class JwtAuthenticationProvider extends DaoAuthenticationProvider {
  18.  
  19. public JwtAuthenticationProvider(UserDetailsService userDetailsService) {
  20. setUserDetailsService(userDetailsService);
  21. }
  22.  
  23. @Override
  24. protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication)
  25. throws AuthenticationException {
  26. if (authentication.getCredentials() == null) {
  27. logger.debug("Authentication failed: no credentials provided");
  28. throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
  29. }
  30.  
  31. String presentedPassword = authentication.getCredentials().toString();
  32. String salt = ((JwtUserDetails) userDetails).getSalt();
  33. // 覆写密码验证逻辑
  34. if (!new PasswordEncoder(salt).matches(userDetails.getPassword(), presentedPassword)) {
  35. logger.debug("Authentication failed: password does not match stored value");
  36. throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
  37. }
  38. }
  39.  
  40. }

用户认证信息查询组件

实现 UserDetailsService 接口,定义用户认证信息查询组件,用于获取认证所需的用户信息和授权信息。

UserDetailsServiceImpl.java

  1. package com.louis.kitty.admin.security;
  2. import java.util.List;
  3. import java.util.Set;
  4. import java.util.stream.Collectors;
  5.  
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.security.core.GrantedAuthority;
  8. import org.springframework.security.core.userdetails.UserDetails;
  9. import org.springframework.security.core.userdetails.UserDetailsService;
  10. import org.springframework.security.core.userdetails.UsernameNotFoundException;
  11. import org.springframework.stereotype.Service;
  12.  
  13. import com.louis.kitty.admin.model.SysUser;
  14. import com.louis.kitty.admin.sevice.SysUserService;
  15.  
  16. /**
  17. * 用户登录认证信息查询
  18. * @author Louis
  19. * @date Nov 20, 2018
  20. */
  21. @Service
  22. public class UserDetailsServiceImpl implements UserDetailsService {
  23.  
  24. @Autowired
  25. private SysUserService sysUserService;
  26.  
  27. @Override
  28. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  29. SysUser user = sysUserService.findByName(username);
  30. if (user == null) {
  31. throw new UsernameNotFoundException("该用户不存在");
  32. }
  33. // 用户权限列表,根据用户拥有的权限标识与如 @PreAuthorize("hasAuthority('sys:menu:view')") 标注的接口对比,决定是否可以调用接口
  34. Set<String> permissions = sysUserService.findPermissions(user.getName());
  35. List<GrantedAuthority> grantedAuthorities = permissions.stream().map(GrantedAuthorityImpl::new).collect(Collectors.toList());
  36. return new JwtUserDetails(user.getName(), user.getPassword(), user.getSalt(), grantedAuthorities);
  37. }
  38. }

用户认证信息封装

上面 UserDetailsService 查询的信息需要封装到实现 UserDetails 接口的封装对象里。

JwtUserDetails.java

  1. package com.louis.kitty.admin.security;
  2. import java.util.Collection;
  3.  
  4. import org.springframework.security.core.GrantedAuthority;
  5. import org.springframework.security.core.userdetails.UserDetails;
  6.  
  7. import com.fasterxml.jackson.annotation.JsonIgnore;
  8.  
  9. /**
  10. * 安全用户模型
  11. * @author Louis
  12. * @date Nov 20, 2018
  13. */
  14. public class JwtUserDetails implements UserDetails {
  15.  
  16. private static final long serialVersionUID = 1L;
  17.  
  18. private String username;
  19. private String password;
  20. private String salt;
  21. private Collection<? extends GrantedAuthority> authorities;
  22.  
  23. JwtUserDetails(String username, String password, String salt, Collection<? extends GrantedAuthority> authorities) {
  24. this.username = username;
  25. this.password = password;
  26. this.salt = salt;
  27. this.authorities = authorities;
  28. }
  29.  
  30. @Override
  31. public String getUsername() {
  32. return username;
  33. }
  34.  
  35. @JsonIgnore
  36. @Override
  37. public String getPassword() {
  38. return password;
  39. }
  40.  
  41. public String getSalt() {
  42. return salt;
  43. }
  44.  
  45. @Override
  46. public Collection<? extends GrantedAuthority> getAuthorities() {
  47. return authorities;
  48. }
  49.  
  50. @JsonIgnore
  51. @Override
  52. public boolean isAccountNonExpired() {
  53. return true;
  54. }
  55.  
  56. @JsonIgnore
  57. @Override
  58. public boolean isAccountNonLocked() {
  59. return true;
  60. }
  61.  
  62. @JsonIgnore
  63. @Override
  64. public boolean isCredentialsNonExpired() {
  65. return true;
  66. }
  67.  
  68. @JsonIgnore
  69. @Override
  70. public boolean isEnabled() {
  71. return true;
  72. }
  73.  
  74. }

登录接口

因为我们没有使用内置的 formLogin 登录处理过滤器,所以需要调用登录认证流程,修改登录接口,加入系统登录认证调用。

SysLoginController.java

  1.    /**
  2. * 登录接口
  3. */
  4. @PostMapping(value = "/login")
  5. public HttpResult login(@RequestBody LoginBean loginBean, HttpServletRequest request) throws IOException {
  6. String username = loginBean.getAccount();
  7. String password = loginBean.getPassword();
  8. String captcha = loginBean.getCaptcha();...
         // 系统登录认证
  9. JwtAuthenticatioToken token = SecurityUtils.login(request, username, password, authenticationManager);
  10.  
  11. return HttpResult.ok(token);
  12. }

Spring Security 的登录认证过程是通过调用 AuthenticationManager 的 authenticate(token) 方法实现的。

登录流程中主要是返回一个认证好的 Authentication 对象,然后保存到上下文供后续进行授权的时候使用。

登录认证成功之后,会利用JWT生成 token 返回给客户端,后续的访问都需要携带此 token 来进行认证。

SecurityUtils.java

  1. /**
  2. * 系统登录认证
  3. * @param request
  4. * @param username
  5. * @param password
  6. * @param authenticationManager
  7. * @return
  8. */
  9. public static JwtAuthenticatioToken login(HttpServletRequest request, String username, String password, AuthenticationManager authenticationManager) {
  10. JwtAuthenticatioToken token = new JwtAuthenticatioToken(username, password);
  11. token.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
  12. // 执行登录认证过程
  13. Authentication authentication = authenticationManager.authenticate(token);
  14. // 认证成功存储认证信息到上下文
  15. SecurityContextHolder.getContext().setAuthentication(authentication);
  16. // 生成令牌并返回给客户端
  17. token.setToken(JwtTokenUtils.generateToken(authentication));
  18. return token;
  19. }

令牌生成器

令牌生成器主要是利用JWT生成所需的令牌,部分代码如下。

JwtTokenUtils.java

  1. /**
  2. * JWT工具类
  3. * @author Louis
  4. * @date Nov 20, 2018
  5. */
  6. public class JwtTokenUtils implements Serializable {
  7.  
  8. /**
  9. * 生成令牌
  10. * @param userDetails 用户
  11. * @return 令牌
  12. */
  13. public static String generateToken(Authentication authentication) {
  14. Map<String, Object> claims = new HashMap<>(3);
  15. claims.put(USERNAME, SecurityUtils.getUsername(authentication));
  16. claims.put(CREATED, new Date());
  17. claims.put(AUTHORITIES, authentication.getAuthorities());
  18. return generateToken(claims);
  19. }
  20.  
  21. /**
  22. * 从数据声明生成令牌
  23. * @param claims 数据声明
  24. * @return 令牌
  25. */
  26. private static String generateToken(Map<String, Object> claims) {
  27. Date expirationDate = new Date(System.currentTimeMillis() + EXPIRE_TIME);
  28. return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, SECRET).compact();
  29. }
  30. }

登录认证过滤器

登录认证过滤器继承 BasicAuthenticationFilter,在访问任何URL的时候会被此过滤器拦截,通过调用 SecurityUtils.checkAuthentication(request) 检查登录状态。

JwtAuthenticationFilter.java

  1. package com.louis.kitty.admin.security;
  2.  
  3. import java.io.IOException;
  4.  
  5. import javax.servlet.FilterChain;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9.  
  10. import org.springframework.beans.factory.annotation.Autowired;
  11. import org.springframework.security.authentication.AuthenticationManager;
  12. import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
  13.  
  14. import com.louis.kitty.admin.util.SecurityUtils;
  15.  
  16. /**
  17. * 登录认证过滤器
  18. * @author Louis
  19. * @date Nov 20, 2018
  20. */
  21. public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
  22.  
  23. @Autowired
  24. public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
  25. super(authenticationManager);
  26. }
  27.  
  28. @Override
  29. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
  30. // 获取token, 并检查登录状态
  31. SecurityUtils.checkAuthentication(request);
  32. chain.doFilter(request, response);
  33. }
  34.  
  35. }

登录认证检查

登录验证检查是通过 SecurityUtils.checkAuthentication(request) 来完成的。

SecurityUtils.java

  1. /**
  2. * 获取令牌进行认证
  3. * @param request
  4. */
  5. public static void checkAuthentication(HttpServletRequest request) {
  6. // 获取令牌并根据令牌获取登录认证信息
  7. Authentication authentication = JwtTokenUtils.getAuthenticationeFromToken(request);
  8. // 设置登录认证信息到上下文
  9. SecurityContextHolder.getContext().setAuthentication(authentication);
  10. }

上面的登录验证是通过 JwtTokenUtils.getAuthenticationeFromToken(request),来验证令牌并返回登录信息的。

JwtTokenUtils.java

  1. /**
  2. * 根据请求令牌获取登录认证信息
  3. * @param token 令牌
  4. * @return 用户名
  5. */
  6. public static Authentication getAuthenticationeFromToken(HttpServletRequest request) {
  7. Authentication authentication = null;
  8. // 获取请求携带的令牌
  9. String token = JwtTokenUtils.getToken(request);
  10. if(token != null) {
  11. // 请求令牌不能为空
  12. if(SecurityUtils.getAuthentication() == null) {
  13. // 上下文中Authentication为空
  14. Claims claims = getClaimsFromToken(token);
  15. if(claims == null) {
  16. return null;
  17. }
  18. String username = claims.getSubject();
  19. if(username == null) {
  20. return null;
  21. }
  22. if(isTokenExpired(token)) {
  23. return null;
  24. }
  25. Object authors = claims.get(AUTHORITIES);
  26. List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
  27. if (authors != null && authors instanceof List) {
  28. for (Object object : (List) authors) {
  29. authorities.add(new GrantedAuthorityImpl((String) ((Map) object).get("authority")));
  30. }
  31. }
  32. authentication = new JwtAuthenticatioToken(username, null, authorities, token);
  33. } else {
  34. if(validateToken(token, SecurityUtils.getUsername())) {
  35. // 如果上下文中Authentication非空,且请求令牌合法,直接返回当前登录认证信息
  36. authentication = SecurityUtils.getAuthentication();
  37. }
  38. }
  39. }
  40. return authentication;
  41. }

清除Shiro配置

清除掉 config 包下的 ShiroConfig 配置类。

清除 oautho2 包下有关 Shiro 的相关代码。

清除掉 sys_token 表和相关操作代码。

源码下载

后端:https://gitee.com/liuge1988/kitty

前端:https://gitee.com/liuge1988/kitty-ui.git


作者:朝雨忆轻尘
出处:https://www.cnblogs.com/xifengxiaoma/
版权所有,欢迎转载,转载请注明原文作者及出处。

Spring Boot + Spring Cloud 实现权限管理系统 后端篇(二十五):Spring Security 版本的更多相关文章

  1. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(十五):系统服务监控

    系统服务监控 新建监控工程 新建Spring Boot项目,取名 kitty-monitor,结构如下. 添加项目依赖 添加 spring boot admin 的相关依赖. pom.xml < ...

  2. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(十九):服务消费(Ribbon、Feign)

    技术背景 上一篇教程中,我们利用Consul注册中心,实现了服务的注册和发现功能,这一篇我们来聊聊服务的调用.单体应用中,代码可以直接依赖,在代码中直接调用即可,但在微服务架构是分布式架构,服务都运行 ...

  3. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(十八):注册中心(Spring Cloud Consul)

    什么是 Consul Consul 是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置.与其它分布式服务注册与发现的方案,Consul 的方案更“一站式”,内置了服务注册与 ...

  4. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(十六):容器部署项目

    容器部署项目 这一章我们引入docker,采用docker容器的方式部署我们的项目. 首先需要有一个linux环境,并且安装 java 和 maven 以及 docker 环境,这个教程多如牛毛,不再 ...

  5. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(十四):项目打包部署

    项目打包部署 安装MySQL镜像 注意:如果使用docker镜像安装MySQL,也需要在前端部署主机安装MySQL,因为备份还原功能是使用MySQL的本地命令进行操作的. 下载镜像 执行以下命令,拉取 ...

  6. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(十二):解决跨域问题

    什么是跨域? 同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源. 同源策略是浏览器安全的基石. 如果一个请求地址里面的协议.域名和端口号都相同,就属于同源. ...

  7. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(十):接口服务整理

    通用操作 通用操作是指一般的增删改查操作,逻辑大体都是一致的,所以统一抽象到CURD接口,需要用到CURD的表直接实现接口就可以了. 通用操作主要有以下几个: 保存操作 /** * 保存操作 * @p ...

  8. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(一):Kitty 系统介绍

    在线演示 演示地址:http://139.196.87.48:9002/kitty 用户名:admin 密码:admin 温馨提示: 有在演示环境删除数据的童鞋们,如果可以的话,麻烦动动小指,右键头像 ...

  9. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(二十三):配置中心(Config、Bus)

    在线演示 演示地址:http://139.196.87.48:9002/kitty 用户名:admin 密码:admin 技术背景 如今微服务架构盛行,在分布式系统中,项目日益庞大,子项目日益增多,每 ...

  10. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(二十):服务熔断(Hystrix、Turbine)

    在线演示 演示地址:http://139.196.87.48:9002/kitty 用户名:admin 密码:admin 雪崩效应 在微服务架构中,由于服务众多,通常会涉及多个服务层级的调用,而一旦基 ...

随机推荐

  1. Sublime Text3快捷键大全

    选择类 Ctrl+D 选中光标所占的文本,继续操作则会选中下一个相同的文本. Alt+F3 选中文本按下快捷键,即可一次性选择全部的相同文本进行同时编辑.举个栗子:快速选中并更改所有相同的变量名.函数 ...

  2. Boost::bind使用详解

    1.Boost::bind 在STL中,我们经常需要使用bind1st,bind2st函数绑定器和fun_ptr,mem_fun等函数适配器,这些函数绑定器和函数适配器使用起来比较麻烦,需要根据是全局 ...

  3. 20172325 2018-2019-2 《Java程序设计》第九周学习总结

    20172325 2018-2019-2 <Java程序设计>第九周学习总结 教材学习内容总结 图的定义 图是由顶点集(VertexSet)和边集(EdgeSet)组成,针对图G,顶点集和 ...

  4. Unity3D使用EasyMovieTexture插件播放视频

    Unity3D对于视频的播放兼容个人感觉很差劲,之前写过一篇使用Unity3D自己自带的一些功能去播放视频,链接如下: http://www.cnblogs.com/xiaoyulong/p/8627 ...

  5. Struts2学习第四天——拦截器及文件上传

    1.概述 Struts2的很多核心功能都是由拦截器完成的. 拦截器很好的实现了AOP的编程思想,在动作的执行之前和结果的返回之后,做拦截处理. 2.struts2的默认拦截器栈 3.自定义拦截器 St ...

  6. spring boot2 集成Redis

    1. 引入依赖 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spr ...

  7. Django URLConf 进阶

    Django处理一个请求 项目启动后根据 settings  ROOT_URLCONF 决定项目根URLconf urlpatterns是django.conf.urls.url()实例的一个Pyth ...

  8. Burpsuite常用模块详解以及渗透测试上的运用

    0x00前言 哪有什么前言,大家好,我是浅安.QQ:320229344... 0x01 JDK的安装,以及Burpsuite的成功开启. burpsuite基于JAVA环境才能正常运行的.所以要先安装 ...

  9. 设计模式总结(Java)—— 单例模式

    1. 定义 为了确保一个类有且仅有一个实例,而且自行实例化并向整个系统提供这个实例. 2. 使用场景 确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有 ...

  10. react-router V4中的url参数

    概述 之前写过react在router中传递数据的2种方法,但是有些细节没有理清楚,现在补上,记录下来,供以后开发时参考,相信对其他人也有用. 参考资料:stackoverflow react rou ...