SpringBoot使用Security认证框架(1.加类)

蚊子 2023年03月02日 669次浏览

前言:

本文章包含Security的认证和授权方法
并且在执行Security之前会执行自已编写的PowerFilter过滤器
而且登录信息会存入Redis,也会从Redis取

本文章只是加各种根据类
使用方法前往:https://www.0po.cn/archives/26

需要注意:

  1. SecurityConfig,45行需要改成你的登录接口地址
  2. LoginUser,需要改动一些东西,注意看注释
  3. PowerFilter,为自已的拦截器,在执行Security之前会执行自已编写的PowerFilter过滤器,可以按需改动,也可不动
  4. 一个个加类过程中,如果那个类里面报错了,不用管,先加完所有类。
  5. 加完后,在看报错的类,不出意外都是引入的包是我的com.zb报的错,改成你的包

依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-security</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>com.alibaba</groupId>
  7. <artifactId>fastjson</artifactId>
  8. <version>1.2.33</version>
  9. </dependency>
  10. <dependency>
  11. <groupId>org.springframework.boot</groupId>
  12. <artifactId>spring-boot-starter-data-redis</artifactId>
  13. </dependency>
  14. <dependency>
  15. <groupId>io.jsonwebtoken</groupId>
  16. <artifactId>jjwt</artifactId>
  17. <version>0.9.0</version>
  18. </dependency>

  19. <dependency>
  20. <groupId>org.projectlombok</groupId>
  21. <artifactId>lombok</artifactId>
  22. <version>1.18.10</version>
  23. </dependency>

需要的工具类

新建config包,第一部分开始

FastJsonRedisSerializer

  1. package com.zb.config;

  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.parser.ParserConfig;
  4. import com.alibaba.fastjson.serializer.SerializerFeature;
  5. import com.fasterxml.jackson.databind.JavaType;
  6. import com.fasterxml.jackson.databind.type.TypeFactory;
  7. import org.springframework.data.redis.serializer.RedisSerializer;
  8. import org.springframework.data.redis.serializer.SerializationException;

  9. import java.nio.charset.Charset;

  10. /**
  11. * Redis使用FastJson序列化
  12. *
  13. * @author sg
  14. */
  15. public class FastJsonRedisSerializer<T> implements RedisSerializer<T>
  16. {

  17. public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

  18. private Class<T> clazz;

  19. static
  20. {
  21. ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
  22. }

  23. public FastJsonRedisSerializer(Class<T> clazz)
  24. {
  25. super();
  26. this.clazz = clazz;
  27. }

  28. @Override
  29. public byte[] serialize(T t) throws SerializationException
  30. {
  31. if (t == null)
  32. {
  33. return new byte[0];
  34. }
  35. return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
  36. }

  37. @Override
  38. public T deserialize(byte[] bytes) throws SerializationException
  39. {
  40. if (bytes == null || bytes.length <= 0)
  41. {
  42. return null;
  43. }
  44. String str = new String(bytes, DEFAULT_CHARSET);

  45. return JSON.parseObject(str, clazz);
  46. }


  47. protected JavaType getJavaType(Class<?> clazz)
  48. {
  49. return TypeFactory.defaultInstance().constructType(clazz);
  50. }
  51. }

JwtUtil

  1. package com.zb.config;

  2. import io.jsonwebtoken.Claims;
  3. import io.jsonwebtoken.JwtBuilder;
  4. import io.jsonwebtoken.Jwts;
  5. import io.jsonwebtoken.SignatureAlgorithm;

  6. import javax.crypto.SecretKey;
  7. import javax.crypto.spec.SecretKeySpec;
  8. import java.util.Base64;
  9. import java.util.Date;
  10. import java.util.UUID;

  11. /**
  12. * JWT工具类
  13. */
  14. public class JwtUtil {

  15. //有效期为
  16. public static final Long JWT_TTL = 60 * 60 *1000L;// 60 * 60 *1000 一个小时
  17. //设置秘钥明文
  18. public static final String JWT_KEY = "abcd";

  19. public static String getUUID(){
  20. String token = UUID.randomUUID().toString().replaceAll("-", "");
  21. return token;
  22. }

  23. /**
  24. * 生成jtw
  25. * @param subject token中要存放的数据(json格式)
  26. * @return
  27. */
  28. public static String createJWT(String subject) {
  29. JwtBuilder builder = getJwtBuilder(subject, null, getUUID());// 设置过期时间
  30. return builder.compact();
  31. }

  32. /**
  33. * 生成jtw
  34. * @param subject token中要存放的数据(json格式)
  35. * @param ttlMillis token超时时间
  36. * @return
  37. */
  38. public static String createJWT(String subject, Long ttlMillis) {
  39. JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());// 设置过期时间
  40. return builder.compact();
  41. }

  42. private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
  43. SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
  44. SecretKey secretKey = generalKey();
  45. long nowMillis = System.currentTimeMillis();
  46. Date now = new Date(nowMillis);
  47. if(ttlMillis==null){
  48. ttlMillis=JwtUtil.JWT_TTL;
  49. }
  50. long expMillis = nowMillis + ttlMillis;
  51. Date expDate = new Date(expMillis);
  52. return Jwts.builder()
  53. .setId(uuid) //唯一的ID
  54. .setSubject(subject) // 主题 可以是JSON数据
  55. .setIssuer("sg") // 签发者
  56. .setIssuedAt(now) // 签发时间
  57. .signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥
  58. .setExpiration(expDate);
  59. }

  60. /**
  61. * 创建token
  62. * @param id
  63. * @param subject
  64. * @param ttlMillis
  65. * @return
  66. */
  67. public static String createJWT(String id, String subject, Long ttlMillis) {
  68. JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);// 设置过期时间
  69. return builder.compact();
  70. }

  71. /**
  72. * 生成加密后的秘钥 secretKey
  73. * @return
  74. */
  75. public static SecretKey generalKey() {
  76. byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
  77. SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
  78. return key;
  79. }

  80. /**
  81. * 解析
  82. *
  83. * @param jwt
  84. * @return
  85. * @throws Exception
  86. */
  87. public static Claims parseJWT(String jwt) throws Exception {
  88. SecretKey secretKey = generalKey();
  89. return Jwts.parser()
  90. .setSigningKey(secretKey)
  91. .parseClaimsJws(jwt)
  92. .getBody();
  93. }


  94. }

RedisConfig

  1. package com.zb.config;

  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.data.redis.connection.RedisConnectionFactory;
  5. import org.springframework.data.redis.core.RedisTemplate;
  6. import org.springframework.data.redis.serializer.StringRedisSerializer;

  7. @Configuration
  8. public class RedisConfig {

  9. @Bean
  10. @SuppressWarnings(value = { "unchecked", "rawtypes" })
  11. public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
  12. {
  13. RedisTemplate<Object, Object> template = new RedisTemplate<>();
  14. template.setConnectionFactory(connectionFactory);

  15. FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class);

  16. // 使用StringRedisSerializer来序列化和反序列化redis的key值
  17. template.setKeySerializer(new StringRedisSerializer());
  18. template.setValueSerializer(serializer);

  19. // Hash的key也采用StringRedisSerializer的序列化方式
  20. template.setHashKeySerializer(new StringRedisSerializer());
  21. template.setHashValueSerializer(serializer);


  22. template.afterPropertiesSet();
  23. return template;
  24. }
  25. }

SecurityConfig,45行需要改成你的登录接口地址


  1. import com.zb.filters.PowerFilter;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.security.authentication.AuthenticationManager;
  6. import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
  7. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  8. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  9. import org.springframework.security.config.http.SessionCreationPolicy;
  10. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  11. import org.springframework.security.web.AuthenticationEntryPoint;
  12. import org.springframework.security.web.access.AccessDeniedHandler;
  13. import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

  14. @Configuration
  15. @EnableGlobalMethodSecurity(prePostEnabled = true)
  16. public class SecurityConfig extends WebSecurityConfigurerAdapter {

  17. @Bean
  18. public BCryptPasswordEncoder cryptPasswordEncoder() {
  19. return new BCryptPasswordEncoder();
  20. }

  21. @Autowired
  22. private PowerFilter powerFilter;

  23. @Autowired
  24. private AuthenticationEntryPoint authenticationEntryPoint;

  25. @Autowired
  26. private AccessDeniedHandler accessDeniedHandler;

  27. @Override
  28. protected void configure(HttpSecurity http) throws Exception {
  29. http
  30. //关闭csrf
  31. .csrf().disable()
  32. //不通过Session获取SecurityContext
  33. .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
  34. .and()
  35. .authorizeRequests()
  36. // 对于登录接口 允许匿名访问(需要改成你的登录接口地址)
  37. .antMatchers("/user/login/**").anonymous()
  38. // 除上面外的所有请求全部需要鉴权认证
  39. .anyRequest().authenticated();
  40. //将用户开发的过滤器添加到用户登陆之前的过滤器上
  41. http.addFilterBefore(powerFilter, UsernamePasswordAuthenticationFilter.class);
  42. //添加自定义异常处理
  43. http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).accessDeniedHandler(accessDeniedHandler);
  44. }

  45. @Bean
  46. public AuthenticationManager createAuthenticationManager() throws Exception {
  47. return super.authenticationManagerBean();
  48. }

  49. }

WebUtils

  1. package com.zb.config;

  2. import javax.servlet.http.HttpServletResponse;
  3. import java.io.IOException;

  4. public class WebUtils
  5. {
  6. /**
  7. * 自定义返回没有认证和授权信息用的
  8. * 将字符串渲染到客户端
  9. *
  10. * @param response 渲染对象
  11. * @param string 待渲染的字符串
  12. * @return null
  13. */
  14. public static String renderString(HttpServletResponse response, String string) {
  15. try
  16. {
  17. response.setStatus(200);
  18. response.setContentType("application/json");
  19. response.setCharacterEncoding("utf-8");
  20. response.getWriter().print(string);
  21. }
  22. catch (IOException e)
  23. {
  24. e.printStackTrace();
  25. }
  26. return null;
  27. }
  28. }

config包结束,第一部分结束


entity包,第二部分开始

LoginUser,需要改动一些东西,注意看注释

  1. package com.zb.entity;

  2. import com.alibaba.fastjson.annotation.JSONField;
  3. import lombok.AllArgsConstructor;
  4. import lombok.Data;
  5. import lombok.NoArgsConstructor;
  6. import org.springframework.security.core.GrantedAuthority;
  7. import org.springframework.security.core.authority.SimpleGrantedAuthority;
  8. import org.springframework.security.core.userdetails.UserDetails;

  9. import java.util.ArrayList;
  10. import java.util.Collection;
  11. import java.util.List;

  12. @Data
  13. @NoArgsConstructor
  14. public class LoginUser implements UserDetails {

  15. //登录信息实体类,换成你的
  16. private User user;
  17. //存放查出来的用户有的权限
  18. private List<String> powers;
  19. //将用户信息和权限全放进去
  20. //User需要换成你的
  21. public LoginUser(User user, List<String> powers) {
  22. this.user = user;
  23. this.powers = powers;
  24. }

  25. @JSONField(serialize = false)
  26. private List<SimpleGrantedAuthority> authorities;

  27. @Override
  28. public Collection<? extends GrantedAuthority> getAuthorities() {
  29. if (authorities != null && authorities.size() > 0) {
  30. return authorities;
  31. }
  32. authorities = new ArrayList<>();
  33. for (String power : powers) {
  34. SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(power);
  35. authorities.add(simpleGrantedAuthority);
  36. }
  37. return authorities;
  38. }

  39. //user.getPasswd()为获取密码,换成你的
  40. @Override
  41. public String getPassword() {
  42. return user.getPasswd();
  43. }

  44. //user.getNickName();为获取用户名,换成你的
  45. @Override
  46. public String getUsername() {
  47. return user.getNickName();
  48. }

  49. @Override
  50. public boolean isAccountNonExpired() {
  51. return true;
  52. }

  53. @Override
  54. public boolean isAccountNonLocked() {
  55. return true;
  56. }

  57. @Override
  58. public boolean isCredentialsNonExpired() {
  59. return true;
  60. }

  61. @Override
  62. public boolean isEnabled() {
  63. return true;
  64. }
  65. }

entity包,第二部分结束


exception包,第三部分开始

AccessDeniedHandlerImpl

  1. package com.zb.exception;

  2. import com.alibaba.fastjson.JSON;
  3. import com.zb.config.WebUtils;
  4. import org.springframework.http.HttpStatus;
  5. import org.springframework.security.access.AccessDeniedException;
  6. import org.springframework.security.web.access.AccessDeniedHandler;
  7. import org.springframework.stereotype.Component;

  8. import javax.servlet.ServletException;
  9. import javax.servlet.http.HttpServletRequest;
  10. import javax.servlet.http.HttpServletResponse;
  11. import java.io.IOException;
  12. import java.util.HashMap;
  13. import java.util.Map;

  14. @Component
  15. public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
  16. @Override
  17. public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
  18. Map<String, Object> result = new HashMap<>();
  19. result.put("msg", "授权失败!");
  20. String json = JSON.toJSONString(result);
  21. WebUtils.renderString(response, json);
  22. }
  23. }

AuthenticationEntryPointImpl

  1. package com.zb.exception;

  2. import com.alibaba.fastjson.JSON;
  3. import com.zb.config.WebUtils;
  4. import org.springframework.http.HttpStatus;
  5. import org.springframework.security.core.AuthenticationException;
  6. import org.springframework.security.web.AuthenticationEntryPoint;
  7. import org.springframework.stereotype.Component;

  8. import javax.servlet.ServletException;
  9. import javax.servlet.http.HttpServletRequest;
  10. import javax.servlet.http.HttpServletResponse;
  11. import java.io.IOException;
  12. import java.util.HashMap;
  13. import java.util.Map;

  14. @Component
  15. public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
  16. @Override
  17. public void commence(HttpServletRequest request, HttpServletResponse response,
  18. AuthenticationException authException) throws IOException, ServletException {
  19. Map<String, Object> result = new HashMap<>();
  20. result.put("code", HttpStatus.UNAUTHORIZED);
  21. result.put("msg", "认证失败!");
  22. String json = JSON.toJSONString(result);
  23. WebUtils.renderString(response, json);

  24. }
  25. }

exception包,第三部分结束


filters包,第四部分开始

PowerFilter,为自已的拦截器,在执行Security之前会执行自已编写的PowerFilter过滤器,可以按需改动,也可不动

  1. package com.zb.filters;

  2. import com.alibaba.fastjson.JSON;
  3. import com.zb.config.JwtUtil;
  4. import com.zb.entity.LoginUser;
  5. import io.jsonwebtoken.Claims;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.data.redis.core.RedisTemplate;
  8. import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  9. import org.springframework.security.core.context.SecurityContextHolder;
  10. import org.springframework.stereotype.Component;
  11. import org.springframework.util.ObjectUtils;
  12. import org.springframework.util.StringUtils;
  13. import org.springframework.web.filter.OncePerRequestFilter;

  14. import javax.servlet.FilterChain;
  15. import javax.servlet.ServletException;
  16. import javax.servlet.http.HttpServletRequest;
  17. import javax.servlet.http.HttpServletResponse;
  18. import java.io.IOException;

  19. @Component
  20. public class PowerFilter extends OncePerRequestFilter {

  21. @Autowired
  22. private RedisTemplate redisTemplate;

  23. @Override
  24. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
  25. String token = request.getHeader("token");
  26. //登陆接口放行
  27. if (!StringUtils.hasText(token)) {
  28. System.out.println("登陆接口放行...");
  29. filterChain.doFilter(request, response);
  30. return;
  31. }
  32. String username = "";
  33. try {//验证令牌的有效性
  34. Claims claims = JwtUtil.parseJWT(token);
  35. username = claims.getSubject();
  36. } catch (Exception e) {
  37. e.printStackTrace();
  38. throw new RuntimeException("认证失败!");
  39. }
  40. //从redis中获取用户信息
  41. Object obj = redisTemplate.boundValueOps("token:" + username).get();
  42. if (ObjectUtils.isEmpty(obj)) {
  43. throw new RuntimeException("认证失败!");
  44. }

  45. LoginUser loginUser = JSON.parseObject(obj.toString(), LoginUser.class);
  46. //登陆的用户存储到上下文中
  47. UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
  48. SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
  49. //转发到下一过滤器或者是控制器
  50. filterChain.doFilter(request, response);
  51. }
  52. }

filters包,第四部分开始

至此,所有工具包结束

使用方法前往:https://www.0po.cn/archives/26