spring security的认证原理请看:https://blog.csdn.net/weixin_43879445/article/details/124093893
我们先看看授权的流程图:
根据特定的业务需求,security使用的功能有所不同。
我这里的主体架构是spring cloud alibaba:2.1.0 、spring cloud:hoxton.SR1
因为是微服务所以写了一个common的工具类和配置类的模块,其它模块maven引入。
直接填代码了:
<!-- lombok 1.18.0 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!--mybatis-plus 3.0.5-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<scope>provided</scope>
</dependency>
<!-- spring-boot-starter-web 2.2.2.RELEASE-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
<!-- hutool工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.6.3</version>
</dependency>
<!--swagger 2.8.0-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<scope>provided</scope>
</dependency>
<!-- Spring Security依赖 2.2.2-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- jwt 0.7.0-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
<!-- redis 2.2.2-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- spring2.X集成redis所需common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.6.0</version>
</dependency>
<!-- 在自定义的处理类里面需要使用 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 配置连接工厂
template.setConnectionFactory(factory);
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
//key序列化方式
template.setKeySerializer(redisSerializer);
//value序列化
template.setValueSerializer(jackson2JsonRedisSerializer);
// 设置hash key 和value序列化模式
template.setHashKeySerializer(redisSerializer);
//value hashmap序列化
template.setHashValueSerializer(jackson2JsonRedisSerializer);
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间600秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
}
spring:
redis:
database: 0
host: 101.xx.xxx.xxx
port: 6379
lettuce:
pool:
max-active: 20
max-idle: 5
max-wait: -1
min-idle: 0
timeout: 1800000
@Component
public class DefaultPasswordEncoder implements PasswordEncoder {
public DefaultPasswordEncoder() {
this(-1);
}
public DefaultPasswordEncoder(int strength) {
}
/**
* 进行MD5加密
*
* @author lixiaolong
* @date 2021/1/22 20:48
*/
@Override
public String encode(CharSequence rawPassword) {
return MD5.encrypt(rawPassword.toString());
}
/**
* 进行密码比对
*
* @author lixiaolong
* @date 2021/1/22 20:48
*/
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return encodedPassword.equals(MD5.encrypt(rawPassword.toString()));
}
}
/**
* 未授权、未认证统一处理类
* Created by hch on 2022/4/1.
*/
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
AuthenticationException e) throws IOException, ServletException {
int status = httpServletResponse.getStatus();
if(status == HttpServletResponse.SC_UNAUTHORIZED) {
Response.out(httpServletResponse, R.unauthentication());
} else {
Response.out(httpServletResponse, R.unauthorized());
}
}
}
/**
* token操作工具类
* Created by Administrator on 2022/4/1.
*/
@Component
public class TokenManager {
//token有效时长 以毫秒为单位
private long tokenEcpiration = 1 * 1 * 60 * 1000;
//编码秘钥
private String tokenSignKey = "xxxxxaaaabbbb";
//1 使用jwt根据用户名生成token
public String createToken(String username) {
String token = Jwts.builder()
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + tokenEcpiration))
.signWith(io.jsonwebtoken.SignatureAlgorithm.HS512, tokenSignKey)
.compressWith(CompressionCodecs.GZIP)
.compact();
return token;
}
//2 根据token字符串得到用户信息
public String getUserInfoFromToken(String token) {
return Jwts.parser()
.setSigningKey(tokenSignKey)
.parseClaimsJws(token).getBody().getSubject();
}
//3 删除token
public void removeToken(String token) {
}
/**
* 判断token是否存在与有效
* @param jwtToken
* @return
*/
public void checkToken(String jwtToken) {
Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(jwtToken);
}
}
/**
* MD5 加密类
*
* @author hch
* @date 2022/3/29 14:29
*/
public final class MD5 {
public static String encrypt(String strSrc) {
try {
char hexChars[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'a', 'b', 'c', 'd', 'e', 'f'};
byte[] bytes = strSrc.getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(bytes);
bytes = md.digest();
int j = bytes.length;
char[] chars = new char[j * 2];
int k = 0;
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
chars[k++] = hexChars[b >>> 4 & 0xf];
chars[k++] = hexChars[b & 0xf];
}
return new String(chars);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("MD5加密出错!!+" + e);
}
}
public static void main(String[] args) {
System.out.println(MD5.encrypt("111111"));
}
}
/**
* 统一返回结果的类
*
* @author hch
* @date 2022/3/29 14:29
*/
@Data
public class R {
private Boolean success;
private Integer code;
private String message;
private Map<String, Object> data = new HashMap<String, Object>();
//把构造方法私有
private R() {
}
//成功静态方法
public static R ok() {
R r = new R();
r.setSuccess(true);
r.setCode(HttpServletResponse.SC_OK);//200
r.setMessage("成功");
return r;
}
//失败静态方法
public static R error() {
R r = new R();
r.setSuccess(false);
r.setCode(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);//500
r.setMessage("失败");
return r;
}
//未经授权 静态方法
public static R unauthorized() {
R r = new R();
r.setSuccess(false);
r.setCode(HttpServletResponse.SC_FORBIDDEN);//403
r.setMessage("未经授权");
return r;
}
//未经认证 静态方法
public static R unauthentication() {
R r = new R();
r.setSuccess(false);
r.setCode(HttpServletResponse.SC_UNAUTHORIZED);//401
r.setMessage("未经认证");
return r;
}
public R success(Boolean success) {
this.setSuccess(success);
return this;
}
public R message(String message) {
this.setMessage(message);
return this;
}
public R code(Integer code) {
this.setCode(code);
return this;
}
public R data(String key, Object value) {
this.data.put(key, value);
return this;
}
public R data(Map<String, Object> map) {
this.setData(map);
return this;
}
}
/**
* 统一返回
*
* @author hch
* @date 2022/3/29 14:29
*/
public class Response {
public static void out(HttpServletResponse response, R r) {
ObjectMapper mapper = new ObjectMapper();
response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
try {
mapper.writeValue(response.getWriter(), r);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* Created by hch on 2022/4/1.
*/
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
//指定出现什么异常执行这个方法
@ExceptionHandler(Exception.class)
@ResponseBody //为了返回数据
public R error(Exception e) {
e.printStackTrace();
return R.error().message("执行了全局异常处理..");
}
//特定异常
@ExceptionHandler(ArithmeticException.class)
@ResponseBody //为了返回数据
public R error(ArithmeticException e) {
e.printStackTrace();
return R.error().message("执行了ArithmeticException异常处理..");
}
//自定义异常
@ExceptionHandler(GuliException.class)
@ResponseBody //为了返回数据
public R error(GuliException e) {
log.error(e.getMessage());
e.printStackTrace();
return R.error().code(e.getCode()).message(e.getMsg());
}
}
/**
* Created by hch on 2022/4/1.
*/
@Data
@AllArgsConstructor //生成有参数构造方法
@NoArgsConstructor //生成无参数构造
public class GuliException extends RuntimeException {
private Integer code;//状态码
private String msg;//异常信息
}
未完。。。