-
Notifications
You must be signed in to change notification settings - Fork 34
使用Crust添加安全认证功能
Yizzuide edited this page Dec 30, 2019
·
8 revisions
1.14.0+
Crust模块基于Spring Security和JWT,可用于在项目中快速接入登录认证功能,提供以下功能:
- 单应用session会话登录认证(如后台管理)、微服务无状态的Token登录。
- 支持BCrypt加密方式、自定义Salt加密。
- 内建支持Light模块缓存获取当前登录用户信息,在session会话方式下默认有超级缓存,在无状态的Token可选开启缓存级别(一级缓存内存池丢弃策略,二级缓存Redis过期方案)。
- 使用门面API模式访问类,让使用更简单。
当前文档使用的版本为2.0.6,使用以下坐标安装:
<dependency>
<groupId>com.github.yizzuide</groupId>
<artifactId>milkomeda-spring-boot-starter</artifactId>
<version>2.0.6</version>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Token方式开启二级缓存下添加需要Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
@EnableCrust
@SpringBootApplication
public class IceServerApplication {
public static void main(String[] args) {
SpringApplication.run(IceServerApplication.class, args);
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements CrustEntity {
private static final long serialVersionUID = -9190491929257431915L;
private String id;
private String username;
@JsonIgnore
private String password;
// 可选字段(自定义salt加密方式时添加)
@JsonIgnore
private String salt;
// 把当前实体`id`字段适配到接口里`uid`的getter
@Override
public String getUID() {
return id;
}
}
public class UserDetailsService extends CrustUserDetailsService {
// Token登录方式下开启缓存需要实现的方法(根据解析token的uid查找实体),session登录方式不需要实现
@Override
protected Serializable findEntityById(String uid) {
// 实际情况下通过Dao查询
// 这里使用BCryptPasswordEncoder模拟实际表中被加密过的字段
return new User("1000", "yiz", new BCryptPasswordEncoder().encode("123456"), null);
}
@Override
protected CrustEntity findEntityByUsername(String username) {
// 实际情况下通过Dao查询,未找到,直接返回null
// 这里使用BCryptPasswordEncoder模拟实际表中被加密过的字段
// 模拟自定义salt字段方式,salt为111222
// return new User("1000", username, new PasswordEncoder("111222").encode("123456"), "111222");
// 模拟BCrypt方式
return new User("1000", username, new BCryptPasswordEncoder().encode("123456"), null);
}
@Override
protected CrustPerm findPermissionsById(String uid, String username) {
// 实际情况下通过Dao查询
CrustPerm crustPerm = new CrustPerm();
crustPerm.setPermNames(Collections.singletonList("ROLE_USER"));
return crustPerm;
}
}
@Configuration
public class WebSecurityConfig extends CrustConfigurerAdapter {
@Bean
public UserDetailsService userDetailsService() {
return new UserDetailsService();
}
// 配置UserDetailsService
@Override
protected void configureProvider(DaoAuthenticationProvider provider) {
provider.setUserDetailsService(userDetailsService());
}
// 配置需要被忽略的URL(默认`/login`已添加到允许访问)
@Override
protected void additionalConfigure(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry urlRegistry, HttpSecurity http) throws Exception {
urlRegistry.antMatchers("/test/**").permitAll()
}
}
配置分为采用对称加密方式和非对称加密方式。对称加密方式比较简单,只需要一个key就能加密与解密,推荐使用;非对称加密(常用RSA)需要使用两个key,私钥用于加密,公钥用于解密。
1.1. 采用对称加密方式生成token的配置
milkomeda:
crust:
# 自定义对称密钥,crust_secure_key是对称密钥值的例子,可以改其它字符串
secure-key: crust_secure_key
# 自定义请求token头字段名,默认为'token'(token值可有`Bearer `前辍,内部会自动判断)
token-name: Authorization
# token过期时间(分钟)
expire: 60
# 不使用BCrypt(如果用户表有salt字段的情况)
#use-bcrypt: false
# 禁用登录用户信息缓存
#enable-cache: false
# enable-cache为true情况下,禁用Redis缓存(只使用内存池丢弃策略缓存)
#enable-cache-l2: false
# 缓存开启时,light模块的配置
light:
# 二级缓存一天后过期
l2-expire: 86400
# 使用时间线丢弃策略
strategy: timeline
1.2. 采用非对称加密方式生成token的配置
milkomeda:
crust:
# 开启非对称方式
use-rsa: true
# RSA私钥
pri-key: MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJr1tdb8ZOqx4UtbEF+cQJvFo1wxMAYOiNKebC1Rw932bCd69GC44gNs7FtKwjAO031vDejXpXk35juK6W4QeytaGuEL1FDHosD2Hye5MTpzUdKy2CrvL4NI+9mtui7nNmcNuA/jkzYtPYxd3cdoCmr45y+DmCnXVaEpFrgCBd6tAgMBAAECgYBuFRGZ6XFTnQw8uTN3iIwJXSzBCJxiIR8n6K1WwJhRbYbFwT4sHAtLfaym6gPrmgy6NhN+jvuZkpF3SSatLv4f3vwu3ToZcmi6A0LlVzFT7cMHBzMP/Ev09aa0N/j9+ykPlJH06ehkvwz/504GEDwLt2791MxWqtZJjuDNWloWQQJBAOaQ+jgUmjIkKX09/x0a8P13JezBP14UV5cZLvoRWW8XzTkfZx/rpC7irpijvcwBhi45kIg8JrIngYm5/QSkLDECQQCsDakrLtIJLenTSHmFi2KfI2EHYT0deFrK92+VDY15iE7gBQBvbiZiAKwjV33gcrtS6JTZWtxKeOAUUPTiUgc9AkEA1Gb+e6dPHZ3+sqfoWxG0rGuU/nRQQgUPY90JT8mn0BXnMxZg1CEqkR62pVtCv6svx2m0Yiy3oSuPxCcYlawAIQJAW5lORjJAGij6gsTkBagmkkjYoIAxdF4eIE7JdhZoCpr6OyQOjkSbZLOs8Yfj+Tm75zDyBiHshC2ERuyu40r+lQJBAJ+SImDYm9NLqo6hq1+FTI1apKyq8rsuQYL8IRsYAHmOGCNmHN1b/AFitHaptZXYOtiZyuEP8xP86Np8vrTUKSg=
# RSA公钥
pub-key: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCa9bXW/GTqseFLWxBfnECbxaNcMTAGDojSnmwtUcPd9mwnevRguOIDbOxbSsIwDtN9bw3o16V5N+Y7iuluEHsrWhrhC9RQx6LA9h8nuTE6c1HSstgq7y+DSPvZrbou5zZnDbgP45M2LT2MXd3HaApq+Ocvg5gp11WhKRa4AgXerQIDAQAB
# 自定义请求token头字段名,默认为'token'(token值可有`Bearer `前辍,内部会自动判断)
token-name: Authorization
# token过期时间(分钟)
expire: 60
# 不使用BCrypt(如果用户表有salt字段的情况)
#use-bcrypt: false
# 禁用登录用户信息缓存
#enable-cache: false
# enable-cache为true情况下,禁用Redis缓存(只使用内存池丢弃策略缓存)
#enable-cache-l2: false
# 缓存开启时,light模块的配置
light:
# 二级缓存一天后过期
l2-expire: 86400
# 使用时间线丢弃策略
strategy: timeline
# 配置session过期时间,默认30m(30分钟)
server:
servlet:
session:
timeout: 120m
milkomeda:
crust:
# 使用session登录方式
stateless: false
# 不使用BCrypt(如果用户表有salt字段的情况)
#use-bcrypt: false
# 自定义登录页面
login-url: /login.html
# 自定义登出处理(登出成功后自动跳转到login-url配置的页面)
logout-url: /logout
下面的使用方式同时适用于Session方式登录和无状态Token认证方式,模块内部根据上面的配置自动区分。
@RestController
public class LoginController {
@PostMapping("login")
public CrustUserInfo<User> login(String username, String password) {
return CrustContext.get().login(username, password, User.class);
}
}
@Slf4j
@RestController
@RequestMapping("case")
public class CaseController {
@GetMapping("info")
// @PreAuthorize("hasAuthority('ROLE_USER')")
// 和上面等同
@PreAuthorize("hasRole('USER')")
public Map<String, Object> info() {
// 获取当前登录用户信息
CrustUserInfo<User> userInfo = CrustContext.getUserInfo(User.class);
log.info("userInfo: {}", userInfo);
Map<String, Object> data = new HashMap<>();
data.put("id", "12345667009874");
data.put("name", "case-01");
// 同一请求线程的多次调用,返回同一个对象
CrustUserInfo<User> userInfo2 = CrustContext.getUserInfo(User.class);
log.info("比较两个对象:{}", userInfo == userInfo2);
return data;
}
}
@RestController
public class LoginController {
@GetMapping("refresh")
public CrustUserInfo<User> refresh() {
CrustUserInfo<User> userInfo = CrustContext.get().getUserInfo(User.class);
String token = CrustContext.get().refreshToken();
userInfo.setToken(token);
return userInfo;
}
}
注意:自动刷新Token机制,不是每隔一段时间一定会刷新Token,内部会根据当前发行的Token时间有效期自动选择是否刷新,如果当前访问的Token已经过期了,还是走认证失败了的流程。
判别式:
token过期时间 - token刷新间隔秒数 < 当前时间
。
milkomeda:
crust:
# 自动刷新Token(默认是开启的,不需要时设置为false)
enableAutoRefreshToken: true
# 刷新间隔(默认5分钟),在这个访问间隔内Token有效情况下可以生成新的Token
refreshTokenInterval: 5
# 设置token刷新响应字段(默认为Authorization)
refreshTokenName: Authorization
# 前端通过获取响应头
Authorization: token的值