Spring Security是Spring提供的一个安全框架,提供认证和授权功能,最主要的是它提供了简单的使用方式,同时又有很高的灵活性,简单,灵活,强大。

我个人博客系统采用的权限框架就是Spring Security,正好整合到SpringCloud里面。

一般系统里关于角色方面通常有这么几张表,角色表、用户-角色表、菜单表、角色-菜单表等。

不过我个人博客系统主要以wordpress作为参考,沿用其12张表,如图:

一、导入Maven依赖

  1. <properties>
  2. <jjwt.version>0.9.</jjwt.version>
  3. <spring-security-jwt.version>1.0..RELEASE</spring-security-jwt.version>
  4. </properties>
  5. <!-- springsecurity-->
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-security</artifactId>
  9. </dependency>
  10. <dependency>
  11. <groupId>org.springframework.security</groupId>
  12. <artifactId>spring-security-jwt</artifactId>
  13. <version>${spring-security-jwt.version}</version>
  14. </dependency>
  15. <dependency>
  16. <groupId>io.jsonwebtoken</groupId>
  17. <artifactId>jjwt</artifactId>
  18. <version>${jjwt.version}</version>
  19. </dependency>

二、编写Spring Security配置类

  1. package com.springcloud.blog.admin.config;
  2. import com.springcloud.blog.admin.security.UserAuthenticationProvider;
  3. import com.springcloud.blog.admin.security.UserPermissionEvaluator;
  4. import com.springcloud.blog.admin.security.handler.*;
  5. import com.springcloud.blog.admin.security.jwt.JWTAuthenticationTokenFilter;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.context.annotation.Configuration;
  9. import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
  10. import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
  11. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  12. import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
  13. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  14. import org.springframework.security.config.http.SessionCreationPolicy;
  15. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  16. import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
  17.  
  18. /**
  19. * SpringSecurity配置类
  20. * @Author youcong
  21. */
  22. @Configuration
  23. @EnableWebSecurity
  24. @EnableGlobalMethodSecurity(prePostEnabled = true) //开启权限注解,默认是关闭的
  25. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  26. /**
  27. * 自定义登录成功处理器
  28. */
  29. @Autowired
  30. private UserLoginSuccessHandler userLoginSuccessHandler;
  31. /**
  32. * 自定义登录失败处理器
  33. */
  34. @Autowired
  35. private UserLoginFailureHandler userLoginFailureHandler;
  36. /**
  37. * 自定义注销成功处理器
  38. */
  39. @Autowired
  40. private UserLogoutSuccessHandler userLogoutSuccessHandler;
  41. /**
  42. * 自定义暂无权限处理器
  43. */
  44. @Autowired
  45. private UserAuthAccessDeniedHandler userAuthAccessDeniedHandler;
  46. /**
  47. * 自定义未登录的处理器
  48. */
  49. @Autowired
  50. private UserAuthenticationEntryPointHandler userAuthenticationEntryPointHandler;
  51. /**
  52. * 自定义登录逻辑验证器
  53. */
  54. @Autowired
  55. private UserAuthenticationProvider userAuthenticationProvider;
  56.  
  57. /**
  58. * 加密方式
  59. * @Author youcong
  60. */
  61. @Bean
  62. public BCryptPasswordEncoder bCryptPasswordEncoder(){
  63. return new BCryptPasswordEncoder();
  64. }
  65. /**
  66. * 注入自定义PermissionEvaluator
  67. */
  68. @Bean
  69. public DefaultWebSecurityExpressionHandler userSecurityExpressionHandler(){
  70. DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
  71. handler.setPermissionEvaluator(new UserPermissionEvaluator());
  72. return handler;
  73. }
  74.  
  75. /**
  76. * 配置登录验证逻辑
  77. */
  78. @Override
  79. protected void configure(AuthenticationManagerBuilder auth){
  80. //这里可启用我们自己的登陆验证逻辑
  81. auth.authenticationProvider(userAuthenticationProvider);
  82. }
  83. /**
  84. * 配置security的控制逻辑
  85. * @Author youcong
  86. * @Param http 请求
  87. */
  88. @Override
  89. protected void configure(HttpSecurity http) throws Exception {
  90.  
  91. http.authorizeRequests()
  92. // 不进行权限验证的请求或资源(从配置文件中读取)
  93. .antMatchers(JWTConfig.antMatchers.split(",")).permitAll()
  94. // .antMatchers("/*").permitAll()
  95. // 其他的需要登陆后才能访问
  96. .anyRequest().authenticated()
  97. .and()
  98. // 配置未登录自定义处理类
  99. .httpBasic().authenticationEntryPoint(userAuthenticationEntryPointHandler)
  100. .and()
  101. // 配置登录地址
  102. .formLogin()
  103. .loginProcessingUrl("/login/userLogin")
  104. // 配置登录成功自定义处理类
  105. .successHandler(userLoginSuccessHandler)
  106. // 配置登录失败自定义处理类
  107. .failureHandler(userLoginFailureHandler)
  108. .and()
  109. // 配置登出地址
  110. .logout()
  111. .logoutUrl("/login/userLogout")
  112. // 配置用户登出自定义处理类
  113. .logoutSuccessHandler(userLogoutSuccessHandler)
  114. .and()
  115. // 配置没有权限自定义处理类
  116. .exceptionHandling().accessDeniedHandler(userAuthAccessDeniedHandler)
  117. .and()
  118. // 开启跨域
  119. .cors()
  120. .and()
  121. // 取消跨站请求伪造防护
  122. .csrf().disable();
  123. // 基于Token不需要session
  124. http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
  125. // 禁用缓存
  126. http.headers().cacheControl();
  127. // 添加JWT过滤器
  128. http.addFilter(new JWTAuthenticationTokenFilter(authenticationManager()));
  129. }
  130. }

三、编写JWTConfig和application.yml增加jwt相关配置

  1. package com.springcloud.blog.admin.config;
  2. import lombok.Getter;
  3. import org.springframework.boot.context.properties.ConfigurationProperties;
  4. import org.springframework.stereotype.Component;
  5.  
  6. /**
  7. * JWT配置类
  8. * @Author youcong
  9. */
  10. @Getter
  11. @Component
  12. @ConfigurationProperties(prefix = "jwt")
  13. public class JWTConfig {
  14. /**
  15. * 密钥KEY
  16. */
  17. public static String secret;
  18. /**
  19. * TokenKey
  20. */
  21. public static String tokenHeader;
  22. /**
  23. * Token前缀字符
  24. */
  25. public static String tokenPrefix;
  26. /**
  27. * 过期时间
  28. */
  29. public static Integer expiration;
  30. /**
  31. * 不需要认证的接口
  32. */
  33. public static String antMatchers;
  34.  
  35. public void setSecret(String secret) {
  36. this.secret = secret;
  37. }
  38.  
  39. public void setTokenHeader(String tokenHeader) {
  40. this.tokenHeader = tokenHeader;
  41. }
  42.  
  43. public void setTokenPrefix(String tokenPrefix) {
  44. this.tokenPrefix = tokenPrefix;
  45. }
  46.  
  47. public void setExpiration(Integer expiration) {
  48. this.expiration = expiration * ;
  49. }
  50.  
  51. public void setAntMatchers(String antMatchers) {
  52. this.antMatchers = antMatchers;
  53. }
  54.  
  55. }

application.yml增加如下内容:

  1. # JWT配置
  2. jwt:
  3. # 密匙KEY
  4. secret: JWTSecret
  5. # HeaderKEY
  6. tokenHeader: Authorization
  7. # Token前缀字符
  8. tokenPrefix: challenger-
  9. # 过期时间 单位秒 1天后过期= 7天后过期=
  10. expiration:
  11. # 配置不需要认证的接口
  12. antMatchers: /index/**,/login/**,/favicon.ico
  13. # 有效时间
  14. validTime: 7

四、编写过滤器处理类

1.UserLoginSuccessHandler.java

  1. package com.springcloud.blog.admin.security.handler;
  2.  
  3. import com.springcloud.blog.admin.config.JWTConfig;
  4. import com.springcloud.blog.admin.security.entity.SelfUserEntity;
  5. import com.springcloud.blog.admin.utils.AccessAddressUtil;
  6. import com.springcloud.blog.admin.utils.JWTTokenUtil;
  7. import com.springcloud.blog.admin.utils.RedisUtil;
  8. import com.springcloud.blog.admin.utils.ResultUtil;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.security.core.Authentication;
  11. import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
  12. import org.springframework.stereotype.Component;
  13.  
  14. import javax.servlet.http.HttpServletRequest;
  15. import javax.servlet.http.HttpServletResponse;
  16. import java.util.HashMap;
  17. import java.util.Map;
  18.  
  19. /**
  20. * @Description 登录成功处理类
  21. * @Author youcong
  22. */
  23. @Component
  24. public class UserLoginSuccessHandler implements AuthenticationSuccessHandler {
  25.  
  26. /**
  27. * 登录成功返回结果
  28. * @Author youcong
  29. */
  30. @Override
  31. public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication){
  32. // 组装JWT
  33. SelfUserEntity selfUserEntity = (SelfUserEntity) authentication.getPrincipal();
  34. String token = JWTTokenUtil.createAccessToken(selfUserEntity);
  35. token = JWTConfig.tokenPrefix + token;
  36.  
  37. // 封装返回参数
  38. Map<String,Object> resultData = new HashMap<>();
  39. resultData.put("code","");
  40. resultData.put("msg", "登录成功");
  41. resultData.put("token",token);
  42. ResultUtil.responseJson(response,resultData);
  43. }
  44. }

2.UserLoginFailureHandler.java

  1. package com.springcloud.blog.admin.security.handler;
  2.  
  3. import com.springcloud.blog.admin.utils.ResultUtil;
  4. import org.springframework.security.authentication.BadCredentialsException;
  5. import org.springframework.security.authentication.LockedException;
  6. import org.springframework.security.core.AuthenticationException;
  7. import org.springframework.security.core.userdetails.UsernameNotFoundException;
  8. import org.springframework.security.web.authentication.AuthenticationFailureHandler;
  9. import org.springframework.stereotype.Component;
  10.  
  11. import javax.servlet.http.HttpServletRequest;
  12. import javax.servlet.http.HttpServletResponse;
  13.  
  14. /**
  15. * @Description 登录失败处理类
  16. * @Author youcong
  17. */
  18. @Component
  19. public class UserLoginFailureHandler implements AuthenticationFailureHandler {
  20. /**
  21. * 登录失败返回结果
  22. * @Author youcong
  23. */
  24. @Override
  25. public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception){
  26. // 这些对于操作的处理类可以根据不同异常进行不同处理
  27. if (exception instanceof UsernameNotFoundException){
  28. System.out.println("【登录失败】"+exception.getMessage());
  29. ResultUtil.responseJson(response,ResultUtil.resultCode(,"用户名不存在"));
  30. }
  31. if (exception instanceof LockedException){
  32. System.out.println("【登录失败】"+exception.getMessage());
  33. ResultUtil.responseJson(response,ResultUtil.resultCode(,"用户被冻结"));
  34. }
  35. if (exception instanceof BadCredentialsException){
  36. System.out.println("【登录失败】"+exception.getMessage());
  37. ResultUtil.responseJson(response,ResultUtil.resultCode(,"密码错误"));
  38. }
  39. ResultUtil.responseJson(response,ResultUtil.resultCode(,"登录失败"));
  40. }
  41. }

3.UserLogoutSuccessHandler.java

  1. package com.springcloud.blog.admin.security.handler;
  2.  
  3. import com.springcloud.blog.admin.utils.DateUtil;
  4. import com.springcloud.blog.admin.utils.RedisUtil;
  5. import com.springcloud.blog.admin.utils.ResultUtil;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.security.core.Authentication;
  8. import org.springframework.security.core.context.SecurityContextHolder;
  9. import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
  10. import org.springframework.stereotype.Component;
  11.  
  12. import javax.servlet.http.HttpServletRequest;
  13. import javax.servlet.http.HttpServletResponse;
  14. import java.util.HashMap;
  15. import java.util.Map;
  16.  
  17. /**
  18. * 登出成功处理类
  19. * @Author youcong
  20. */
  21. @Component
  22. public class UserLogoutSuccessHandler implements LogoutSuccessHandler {
  23.  
  24. /**
  25. * 用户登出返回结果
  26. * 这里应该让前端清除掉Token
  27. * @Author youcong
  28. */
  29. @Override
  30. public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication){
  31.  
  32. Map<String,Object> resultData = new HashMap<>();
  33. resultData.put("code","");
  34. resultData.put("msg", "登出成功");
  35. SecurityContextHolder.clearContext();
  36. ResultUtil.responseJson(response,ResultUtil.resultSuccess(resultData));
  37. }
  38. }

4.UserAuthAccessDeniedHandler.java

  1. package com.springcloud.blog.admin.security.handler;
  2. import com.springcloud.blog.admin.utils.ResultUtil;
  3. import org.springframework.security.access.AccessDeniedException;
  4. import org.springframework.security.web.access.AccessDeniedHandler;
  5. import org.springframework.stereotype.Component;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8.  
  9. /**
  10. * @Description 暂无权限处理类
  11. * @Author youcong
  12. */
  13. @Component
  14. public class UserAuthAccessDeniedHandler implements AccessDeniedHandler {
  15. /**
  16. * 暂无权限返回结果
  17. * @Author youcong
  18. */
  19. @Override
  20. public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exception){
  21. ResultUtil.responseJson(response,ResultUtil.resultCode(,"未授权"));
  22. }
  23. }

5.UserAuthenticationEntryPointHandler.java

  1. package com.springcloud.blog.admin.security.handler;
  2.  
  3. import com.springcloud.blog.admin.utils.ResultUtil;
  4. import org.springframework.security.core.AuthenticationException;
  5. import org.springframework.security.web.AuthenticationEntryPoint;
  6. import org.springframework.stereotype.Component;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9.  
  10. /**
  11. * 用户未登录处理类
  12. * @Author youcong
  13. */
  14. @Component
  15. public class UserAuthenticationEntryPointHandler implements AuthenticationEntryPoint {
  16. /**
  17. * 用户未登录返回结果
  18. * @Author youcong
  19. */
  20. @Override
  21. public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception){
  22. ResultUtil.responseJson(response,ResultUtil.resultCode(,"未登录"));
  23. }
  24. }

6.UserAuthenticationProvider.java

自定义登录验证这个类,需要根据实际情况重写。通常来说改动不大。

  1. package com.springcloud.blog.admin.security;
  2.  
  3. import com.baomidou.mybatisplus.mapper.EntityWrapper;
  4. import com.springcloud.blog.admin.entity.Usermeta;
  5. import com.springcloud.blog.admin.entity.Users;
  6. import com.springcloud.blog.admin.security.entity.SelfUserEntity;
  7. import com.springcloud.blog.admin.service.UsermetaService;
  8. import com.springcloud.blog.admin.service.UsersService;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.security.authentication.AuthenticationProvider;
  11. import org.springframework.security.authentication.BadCredentialsException;
  12. import org.springframework.security.authentication.LockedException;
  13. import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  14. import org.springframework.security.core.Authentication;
  15. import org.springframework.security.core.AuthenticationException;
  16. import org.springframework.security.core.GrantedAuthority;
  17. import org.springframework.security.core.authority.SimpleGrantedAuthority;
  18. import org.springframework.security.core.userdetails.UsernameNotFoundException;
  19. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  20. import org.springframework.stereotype.Component;
  21. import java.util.HashSet;
  22. import java.util.List;
  23. import java.util.Set;
  24.  
  25. /**
  26. * 自定义登录验证
  27. *
  28. * @Author youcong
  29. */
  30. @Component
  31. public class UserAuthenticationProvider implements AuthenticationProvider {
  32.  
  33. @Autowired
  34. private UsersService usersService;
  35.  
  36. @Autowired
  37. private UsermetaService usermetaService;
  38.  
  39. @Override
  40. public Authentication authenticate(Authentication authentication) throws AuthenticationException {
  41. // 获取表单输入中返回的用户名
  42. String userName = (String) authentication.getPrincipal();
  43. // 获取表单中输入的密码
  44. String password = (String) authentication.getCredentials();
  45. // 查询用户是否存在
  46. SelfUserEntity userInfo = usersService.getUserInfo(userName);
  47.  
  48. if (userInfo.getUsername() == null || userInfo.getUsername() == "") {
  49. throw new UsernameNotFoundException("用户名不存在");
  50. }
  51.  
  52. // 我们还要判断密码是否正确,这里我们的密码使用BCryptPasswordEncoder进行加密的
  53. if (!new BCryptPasswordEncoder().matches(password, userInfo.getPassword())) {
  54. throw new BadCredentialsException("密码不正确");
  55. }
  56. // 还可以加一些其他信息的判断,比如用户账号已停用等判断
  57. if (userInfo.getStatus().equals("")) {
  58. throw new LockedException("该用户已被冻结");
  59. }
  60. // 角色集合
  61. Set<GrantedAuthority> authorities = new HashSet<>();
  62.  
  63. EntityWrapper<Usermeta> roleWrapper = new EntityWrapper<>();
  64. roleWrapper.eq("user_id",userInfo.getUserId());
  65. roleWrapper.eq("meta_key","wp_user_level");
  66. // 查询用户角色
  67. List<Usermeta> sysRoleEntityList = usermetaService.selectList(roleWrapper);
  68. for (Usermeta sysRoleEntity: sysRoleEntityList){
  69. authorities.add(new SimpleGrantedAuthority("ROLE_" + sysRoleEntity.getMetaValue()));
  70. }
  71. userInfo.setAuthorities(authorities);
  72. // 进行登录
  73. return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
  74. }
  75.  
  76. @Override
  77. public boolean supports(Class<?> authentication) {
  78. return true;
  79. }
  80. }

7.UserPermissionEvaluator.java

  1. package com.springcloud.blog.admin.security;
  2.  
  3. import com.baomidou.mybatisplus.mapper.EntityWrapper;
  4. import com.springcloud.blog.admin.entity.Usermeta;
  5. import com.springcloud.blog.admin.service.UsermetaService;
  6. import org.apache.catalina.User;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.security.access.PermissionEvaluator;
  9. import org.springframework.security.core.Authentication;
  10. import org.springframework.stereotype.Component;
  11. import java.io.Serializable;
  12. import java.util.HashSet;
  13. import java.util.List;
  14. import java.util.Set;
  15.  
  16. /**
  17. * 自定义权限注解验证
  18. * @Author youcong
  19. */
  20. @Component
  21. public class UserPermissionEvaluator implements PermissionEvaluator {
  22.  
  23. @Autowired
  24. private UsermetaService usermetaService;
  25.  
  26. /**
  27. * hasPermission鉴权方法
  28. * 这里仅仅判断PreAuthorize注解中的权限表达式
  29. * 实际中可以根据业务需求设计数据库通过targetUrl和permission做更复杂鉴权
  30. * 当然targetUrl不一定是URL可以是数据Id还可以是管理员标识等,这里根据需求自行设计
  31. * @Author youcong
  32. * @Param authentication 用户身份(在使用hasPermission表达式时Authentication参数默认会自动带上)
  33. * @Param targetUrl 请求路径
  34. * @Param permission 请求路径权限
  35. * @Return boolean 是否通过
  36. */
  37. @Override
  38. public boolean hasPermission(Authentication authentication, Object targetUrl, Object permission) {
  39. // 获取用户信息
  40. Usermeta selfUserEntity =(Usermeta) authentication.getPrincipal();
  41. // 查询用户权限(这里可以将权限放入缓存中提升效率)
  42. Set<String> permissions = new HashSet<>();
  43. EntityWrapper<Usermeta> roleWrapper = new EntityWrapper<>();
  44. roleWrapper.eq("user_id",selfUserEntity.getUserId());
  45. roleWrapper.eq("meta_key","wp_user_level");
  46. List<Usermeta> sysMenuEntityList = usermetaService.selectList(roleWrapper);
  47. for (Usermeta sysMenuEntity:sysMenuEntityList) {
  48. permissions.add(sysMenuEntity.getMetaValue());
  49. }
  50. // 权限对比
  51. if (permissions.contains(permission.toString())){
  52. return true;
  53. }
  54. return true;
  55. }
  56. @Override
  57. public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
  58. return false;
  59. }
  60. }

五、编写实体类

  1. package com.springcloud.blog.admin.security.entity;
  2.  
  3. import org.springframework.security.core.GrantedAuthority;
  4. import org.springframework.security.core.userdetails.UserDetails;
  5.  
  6. import java.io.Serializable;
  7. import java.util.Collection;
  8. import java.util.Map;
  9.  
  10. /**
  11. * SpringSecurity用户的实体
  12. * 注意:这里必须要实现UserDetails接口
  13. *
  14. * @Author youcong
  15. */
  16. public class SelfUserEntity implements Serializable, UserDetails {
  17.  
  18. private static final long serialVersionUID = 1L;
  19.  
  20. /**
  21. * 用户ID
  22. */
  23. private Long userId;
  24. /**
  25. * 用户名
  26. */
  27. private String username;
  28. /**
  29. * 密码
  30. */
  31. private String password;
  32. /**
  33. * 状态
  34. */
  35. private String status;
  36.  
  37. /**
  38. * 显示名称
  39. */
  40. private String displayName;
  41.  
  42. /**
  43. * 用户参数
  44. */
  45. private Map<String, String> userParamMap;
  46.  
  47. /**
  48. * 用户角色
  49. */
  50. private Collection<GrantedAuthority> authorities;
  51. /**
  52. * 账户是否过期
  53. */
  54. private boolean isAccountNonExpired = false;
  55. /**
  56. * 账户是否被锁定
  57. */
  58. private boolean isAccountNonLocked = false;
  59. /**
  60. * 证书是否过期
  61. */
  62. private boolean isCredentialsNonExpired = false;
  63. /**
  64. * 账户是否有效
  65. */
  66. private boolean isEnabled = true;
  67.  
  68. public static long getSerialVersionUID() {
  69. return serialVersionUID;
  70. }
  71.  
  72. public Long getUserId() {
  73. return userId;
  74. }
  75.  
  76. public void setUserId(Long userId) {
  77. this.userId = userId;
  78. }
  79.  
  80. @Override
  81. public String getUsername() {
  82. return username;
  83. }
  84.  
  85. public void setUsername(String username) {
  86. this.username = username;
  87. }
  88.  
  89. @Override
  90. public String getPassword() {
  91. return password;
  92. }
  93.  
  94. public void setPassword(String password) {
  95. this.password = password;
  96. }
  97.  
  98. public void setAuthorities(Collection<GrantedAuthority> authorities) {
  99. this.authorities = authorities;
  100. }
  101.  
  102. public void setEnabled(boolean enabled) {
  103. isEnabled = enabled;
  104. }
  105.  
  106. public void setStatus(String status) {
  107. this.status = status;
  108. }
  109.  
  110. public String getStatus() {
  111. return status;
  112. }
  113.  
  114. public String getDisplayName() {
  115. return displayName;
  116. }
  117.  
  118. public void setDisplayName(String displayName) {
  119. this.displayName = displayName;
  120. }
  121.  
  122. public Map<String, String> getUserParamMap() {
  123. return userParamMap;
  124. }
  125.  
  126. public void setUserParamMap(Map<String, String> userParamMap) {
  127. this.userParamMap = userParamMap;
  128. }
  129.  
  130. @Override
  131. public Collection<GrantedAuthority> getAuthorities() {
  132. return authorities;
  133. }
  134.  
  135. @Override
  136. public boolean isAccountNonExpired() {
  137. return isAccountNonExpired;
  138. }
  139.  
  140. @Override
  141. public boolean isAccountNonLocked() {
  142. return isAccountNonLocked;
  143. }
  144.  
  145. @Override
  146. public boolean isCredentialsNonExpired() {
  147. return isCredentialsNonExpired;
  148. }
  149.  
  150. @Override
  151. public boolean isEnabled() {
  152. return isEnabled;
  153. }
  154.  
  155. }

六、编写JWT接口请求拦截器

  1. package com.springcloud.blog.admin.security.jwt;
  2.  
  3. import com.alibaba.fastjson.JSON;
  4. import com.alibaba.fastjson.JSONObject;
  5. import com.springcloud.blog.admin.config.JWTConfig;
  6. import com.springcloud.blog.admin.security.entity.SelfUserEntity;
  7. import com.springcloud.blog.admin.utils.CollectionUtil;
  8. import com.springcloud.blog.admin.utils.JWTTokenUtil;
  9. import com.springcloud.blog.admin.utils.RedisUtil;
  10. import io.jsonwebtoken.Claims;
  11. import io.jsonwebtoken.ExpiredJwtException;
  12. import io.jsonwebtoken.Jwts;
  13. import org.springframework.beans.factory.annotation.Autowired;
  14. import org.springframework.security.authentication.AuthenticationManager;
  15. import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  16. import org.springframework.security.core.GrantedAuthority;
  17. import org.springframework.security.core.authority.SimpleGrantedAuthority;
  18. import org.springframework.security.core.context.SecurityContextHolder;
  19. import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
  20. import org.springframework.util.StringUtils;
  21.  
  22. import javax.servlet.FilterChain;
  23. import javax.servlet.ServletException;
  24. import javax.servlet.http.HttpServletRequest;
  25. import javax.servlet.http.HttpServletResponse;
  26. import java.io.IOException;
  27. import java.util.ArrayList;
  28. import java.util.List;
  29. import java.util.Map;
  30.  
  31. /**
  32. * JWT接口请求校验拦截器
  33. * 请求接口时会进入这里验证Token是否合法和过期
  34. *
  35. * @Author youcong
  36. */
  37. public class JWTAuthenticationTokenFilter extends BasicAuthenticationFilter {
  38.  
  39. public JWTAuthenticationTokenFilter(AuthenticationManager authenticationManager) {
  40. super(authenticationManager);
  41. }
  42.  
  43. @Override
  44. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
  45. // 获取请求头中JWT的Token
  46. String tokenHeader = request.getHeader(JWTConfig.tokenHeader);
  47.  
  48. if (null != tokenHeader && tokenHeader.startsWith(JWTConfig.tokenPrefix)) {
  49. try {
  50.  
  51. // 截取JWT前缀
  52. String token = tokenHeader.replace(JWTConfig.tokenPrefix, "");
  53. // 解析JWT
  54. Claims claims = Jwts.parser()
  55. .setSigningKey(JWTConfig.secret)
  56. .parseClaimsJws(token)
  57. .getBody();
  58. // 获取用户名
  59. String username = claims.getSubject();
  60. String userId = claims.getId();
  61.  
  62. if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(userId)) {
  63. // 获取角色
  64. List<GrantedAuthority> authorities = new ArrayList<>();
  65. String authority = claims.get("authorities").toString();
  66. if (!StringUtils.isEmpty(authority)) {
  67. List<Map<String, String>> authorityMap = JSONObject.parseObject(authority, List.class);
  68. for (Map<String, String> role : authorityMap) {
  69. if (!StringUtils.isEmpty(role)) {
  70. authorities.add(new SimpleGrantedAuthority(role.get("authority")));
  71. }
  72. }
  73. }
  74. //组装参数
  75. SelfUserEntity selfUserEntity = new SelfUserEntity();
  76. selfUserEntity.setUsername(claims.getSubject());
  77. selfUserEntity.setUserId(Long.parseLong(claims.getId()));
  78. selfUserEntity.setAuthorities(authorities);
  79. UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(selfUserEntity, userId, authorities);
  80. SecurityContextHolder.getContext().setAuthentication(authentication);
  81. }
  82. } catch (ExpiredJwtException e) {
  83. System.out.println("Token过期");
  84. } catch (Exception e) {
  85. System.out.println("Token无效");
  86. }
  87. }
  88. filterChain.doFilter(request, response);
  89. return;
  90. }
  91. }

七、SpringSecurity用户的业务实现

  1. package com.springcloud.blog.admin.security.service;
  2.  
  3. import com.baomidou.mybatisplus.mapper.EntityWrapper;
  4. import com.springcloud.blog.admin.entity.Users;
  5. import com.springcloud.blog.admin.security.entity.SelfUserEntity;
  6. import com.springcloud.blog.admin.service.UsersService;
  7. import org.springframework.beans.BeanUtils;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.security.core.userdetails.UserDetailsService;
  10. import org.springframework.security.core.userdetails.UsernameNotFoundException;
  11. import org.springframework.stereotype.Component;
  12.  
  13. /**
  14. * SpringSecurity用户的业务实现
  15. *
  16. * @Author youcong
  17. */
  18. @Component
  19. public class SelfUserDetailsService implements UserDetailsService {
  20.  
  21. @Autowired
  22. private UsersService usersService;
  23.  
  24. /**
  25. * 查询用户信息
  26. *
  27. * @Author youcong
  28. * @Param username 用户名
  29. * @Return UserDetails SpringSecurity用户信息
  30. */
  31. @Override
  32. public SelfUserEntity loadUserByUsername(String username) throws UsernameNotFoundException {
  33.  
  34. EntityWrapper<Users> wrapper = new EntityWrapper<>();
  35.  
  36. //邮箱正则表达式
  37. String expr = "^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})$";
  38.  
  39. //是否为邮箱
  40. if (username.matches(expr)) {
  41. wrapper.eq("user_email", username);
  42. } else {
  43. wrapper.eq("user_login", username);
  44. }
  45.  
  46. // 查询用户信息
  47. Users sysUserEntity = usersService.selectOne(wrapper);
  48. if (sysUserEntity != null) {
  49. // 组装参数
  50. SelfUserEntity selfUserEntity = new SelfUserEntity();
  51. BeanUtils.copyProperties(sysUserEntity, selfUserEntity);
  52. return selfUserEntity;
  53. }
  54. return null;
  55. }
  56. }

八、Spring Security常用注解

1.@Secured

当@EnableGlobalMethodSecurity(securedEnabled=true)的时候,@Secured可以使用。

  1. @PostMapping("/helloUser")
  2. @Secured({"ROLE_normal","ROLE_admin"})
  3. public Map<String, Object> initDashboard() {
  4. Map<String, Object> result = new HashMap<>();
  5. result.put(ResponseDict.RESPONSE_TITLE_KEY, "仪表盘初始化");
  6. result.put(ResponseDict.RESPONSE_DATA_KEY, dashboardService.initDashboard());
  7. return ResultUtil.resultSuccess(result);
  8. }

说明:拥有normal或者admin角色的用户都可以方法helloUser()方法。另外需要注意的是这里匹配的字符串需要添加前缀“ROLE_“。

2.@PreAuthorize

Spring的 @PreAuthorize/@PostAuthorize 注解更适合方法级的安全,也支持Spring 表达式语言,提供了基于表达式的访问控制。

当@EnableGlobalMethodSecurity(prePostEnabled=true)的时候,@PreAuthorize可以使用:

  1. @PostMapping("/initDashboard")
  2. @PreAuthorize("hasRole('100')")
  3. public Map<String, Object> initDashboard() {
  4. Map<String, Object> result = new HashMap<>();
  5. result.put(ResponseDict.RESPONSE_TITLE_KEY, "仪表盘初始化");
  6. result.put(ResponseDict.RESPONSE_DATA_KEY, dashboardService.initDashboard());
  7. return ResultUtil.resultSuccess(result);
  8. }

3.@PostAuthorize

@PostAuthorize 注解使用并不多,在方法执行后再进行权限验证,适合验证带有返回值的权限,Spring EL 提供 返回对象能够在表达式语言中获取返回的对象returnObject。

当@EnableGlobalMethodSecurity(prePostEnabled=true)的时候,@PostAuthorize可以使用:

  1. @GetMapping("/getUserInfo")
  2. @PostAuthorize(" returnObject!=null && returnObject.username == authentication.name")
  3. public User getUserInfo() {
  4. Object pricipal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
  5. User user;
  6. if("anonymousUser".equals(pricipal)) {
  7. user = null;
  8. }else {
  9. user = (User) pricipal;
  10. }
  11. return user;
  12. }

九、测试

(1)登录测试,拿到token,如图:

(2)请求中如果不携带token的话,请求其它接口就会显示没有登录的提示,如图:

(3)正确的请求应当携带token,就像下面这样,如图:

(4)没有权限请求,如图:

SpringCloud之Security的更多相关文章

  1. springcloud +spring security多种验证方式之第三方token生成自己的token通过校验和自己的表单验证大体流程

    步骤: 1.继承 WebSecurityConfigurerAdapter.class,其中使用两个过滤器,一个spring scurity自带的UsernamePasswordAuthenticat ...

  2. SpringCloud集成Security安全(Eureka注册中心)

    1.说明 为了保护注册中心的服务安全, 避免恶意服务注册到Eureka, 需要对Eureka Server进行安全保护, 本文基于Spring Security方案, 为Eureka Server增加 ...

  3. SpringCloud集成Security安全(Config配置中心)

    1.说明 为了保护配置中心的敏感数据, 需要对Config Server进行安全保护, 本文基于Spring Security方案, 为Config Server增加最简单的Basic安全认证. 2. ...

  4. spring security 原理+实战

    疯狂创客圈 Java 高并发[ 亿级流量聊天室实战]实战系列 [博客园总入口 ] 架构师成长+面试必备之 高并发基础书籍 [Netty Zookeeper Redis 高并发实战 ] 前言 Crazy ...

  5. 学习一下 Spring Security

    一.Spring Security 1.什么是 Spring Security? (1)基本认识 Spring Security 是基于 Spring 框架,用于解决 Web 应用安全性的 一种方案, ...

  6. SpringBootSecurity学习(14)前后端分离版之 OAuth2.0介绍

    登录总结 前面基本介绍了security的常规用法,同时介绍了JWT和它的一个简单实现,基本上开发中遇到的登录问题都能解决了,即使在分布式开发,或者微服务开发中实现登录也基本没有问题了.securit ...

  7. SpringSession 独立使用

    疯狂创客圈 Java 高并发[ 亿级流量聊天室实战]实战系列 [博客园总入口 ] 架构师成长+面试必备之 高并发基础书籍 [Netty Zookeeper Redis 高并发实战 ] 前言 Crazy ...

  8. WebService安全机制的思考与实践

    近来因业务需要,需要研究webservice,于是便有这篇文章:SpringBoot整合Apache-CXF实践 一.WebService是什么? WebService是一个平台独立的.低耦合的.自包 ...

  9. SpringCloud微服务实战——搭建企业级开发框架(四十):使用Spring Security OAuth2实现单点登录(SSO)系统

    一.单点登录SSO介绍   目前每家企业或者平台都存在不止一套系统,由于历史原因每套系统采购于不同厂商,所以系统间都是相互独立的,都有自己的用户鉴权认证体系,当用户进行登录系统时,不得不记住每套系统的 ...

随机推荐

  1. 如何理解golang中的nil

    nil的奇怪行为 刚接触golang时,发现nil在不同的上下文,行为表现是不同的,并且和其他语言中的表现,也不大相同 实例1:输入true, true, false,不符合传递性 func main ...

  2. c++实现lower_bound和upper_bound

    #include <bits/stdc++.h> using namespace std; int a[] = {0,1,3,3,5,6,7,8,9,20,21,21,21,30,41,4 ...

  3. 201771010128王玉兰《面向对象与程序设计(java)第十五周学习总结》

    第一部分:理论知识 一:JAR文件 Java程序的打包:程序编译完成后,程序员 将.class文件压缩打包为.jar文件后,GUI界面 程序就可以直接双击图标运行. jar文件(Java归档)既可以包 ...

  4. Spring 中的事件处理

    Spring 中的事件处理 Spring 的核心是 ApplicationContext,它负责管理 beans 的完整生命周期.当加载 beans 时,ApplicationContext 发布某些 ...

  5. Spring 注入内部 Beans

    注入内部 Beans inner beans 是在其他 bean 的范围内定义的 bean. 下面是一个基于setter注入的内部 bean 进行配置的配置文件 Beans.xml 文件: <? ...

  6. 浅析String、StringBuilder、StringBuffer

    谈谈我对 String.StringBuilder.StringBuffer 的理解 StringBuilder.StringBuffer 和 String 一样,都是用于存储字符串的. 1.那既然有 ...

  7. 2017-ACM南宁网络赛

    In this problem, we will define a graph called star graph, and the question is to find the minimum d ...

  8. CF894C Marco and GCD Sequence

    题目链接:http://codeforces.com/contest/894/problem/C 题目大意: 按照严格递增的顺序给出 \(m\) 个数作为公因数集,请你构造出一个数列,对于数列中的任意 ...

  9. python运用 - log信息提取(知识: 遍历 | os )

    运用到的python知识点: excel相关:https://www.cnblogs.com/yaner2018/p/11269873.html 字典: python字典的几种方式: 1)key值遍历 ...

  10. U-Learning 后端开发日志(建设中...)

    目录 U-Learning--基于泛在学习的教学系统 项目背景 技术栈 框架 中间件 插件 里程碑 CentOS 7搭建JAVA开发环境 接口参数校验(不使用hibernate-validator,规 ...