- pojo层
- security 配置类
- security工具类
- controller:
- service:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OnlineUser {
private String userName;
private String nickName;
private String job;
private String browser;
private String ip;
private String address;
private String key;
private Date loginTime;
}
security 配置类
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final TokenProvider tokenProvider;
private final CorsFilter corsFilter;
private final JwtAuthenticationEntryPoint authenticationErrorHandler;
private final JwtAccessDeniedHandler jwtAccessDeniedHandler;
private final ApplicationContext applicationContext;
public SecurityConfig(TokenProvider tokenProvider, CorsFilter corsFilter, JwtAuthenticationEntryPoint authenticationErrorHandler, JwtAccessDeniedHandler jwtAccessDeniedHandler, ApplicationContext applicationContext) {
this.tokenProvider = tokenProvider;
this.corsFilter = corsFilter;
this.authenticationErrorHandler = authenticationErrorHandler;
this.jwtAccessDeniedHandler = jwtAccessDeniedHandler;
this.applicationContext = applicationContext;
}
@Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
// 去除 ROLE_ 前缀
return new GrantedAuthorityDefaults("");
}
@Bean
public PasswordEncoder passwordEncoder() {
// 密码加密方式
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// 搜寻匿名标记 url: @AnonymousAccess
Map handlerMethodMap = applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();
Set anonymousUrls = new HashSet<>();
for (Map.Entry infoEntry : handlerMethodMap.entrySet()) {
HandlerMethod handlerMethod = infoEntry.getValue();
AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);
if (null != anonymousAccess) {
anonymousUrls.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
}
}
httpSecurity
// 禁用 CSRF
.csrf().disable()
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
// 授权异常
.exceptionHandling()
.authenticationEntryPoint(authenticationErrorHandler)
.accessDeniedHandler(jwtAccessDeniedHandler)
// 防止iframe 造成跨域
.and()
.headers()
.frameOptions()
.disable()
// 不创建会话
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// 静态资源等等
.antMatchers(
HttpMethod.GET,
"*.html",
"*.css",
"*.js",
"/webSocketapi-docs").permitAll()
.antMatchers("/v2/api-docs-ext").permitAll()
//.antMatchers("/api/wxmp
public static String getUsername() {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
throw new BadRequestException(HttpStatus.UNAUTHORIZED, "当前登录状态过期");
}
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
return userDetails.getUsername();
}
public static Long getUserId() {
Object obj = getUserDetails();
JSONObject json = new JSONObject(obj);
return json.get("id", Long.class);
}
}
controller:
@RestController
@RequestMapping("/auth")
@Api(tags = "系统:系统授权接口")
public class AuthController {
@Value("${loginCode.expiration}")
private Long expiration;
@Value("${rsa.private_key}")
private String privateKey;
@Value("${single.login}")
private Boolean singleLogin;
private final SecurityProperties properties;
private final RedisUtils redisUtils;
private final UserDetailsService userDetailsService;
private final OnlineUserService onlineUserService;
private final TokenProvider tokenProvider;
private final AuthenticationManagerBuilder authenticationManagerBuilder;
public AuthController(SecurityProperties properties, RedisUtils redisUtils, UserDetailsService userDetailsService, OnlineUserService onlineUserService, TokenProvider tokenProvider, AuthenticationManagerBuilder authenticationManagerBuilder) {
this.properties = properties;
this.redisUtils = redisUtils;
this.userDetailsService = userDetailsService;
this.onlineUserService = onlineUserService;
this.tokenProvider = tokenProvider;
this.authenticationManagerBuilder = authenticationManagerBuilder;
}
@Log("用户登录")
@ApiOperation("登录授权")
@AnonymousAccess
// @AnonymousAccess是自定义注解,用于表示可以匿名访问的方法
@PostMapping(value = "/login")
public ResponseEntity
service:
获得在线用户列表,与当前登录的用户进行匹配,如果匹配到相同的,则删除之前的
public void checkLoginOnUser(String userName, String igoreToken) {
List onlineUsers = getAll(userName, 0);
if (onlineUsers == null || onlineUsers.isEmpty()) {
return;
}
for (OnlineUser onlineUser : onlineUsers) {
if (onlineUser.getUserName().equals(userName)) {
try {
String token = EncryptUtils.desDecrypt(onlineUser.getKey());
if (StringUtils.isNotBlank(igoreToken) && !igoreToken.equals(token)) {
this.kickOut(onlineUser.getKey());
} else if (StringUtils.isBlank(igoreToken)) {
this.kickOut(onlineUser.getKey());
}
} catch (Exception e) {
log.error("checkUser is error", e);
}
}
}
}
}
public void kickOut(String key) throws Exception {
key = properties.getOnlineKey() + EncryptUtils.desDecrypt(key);
redisUtils.del(key);
}
public void logout(String token) {
String key = properties.getOnlineKey() + token;
redisUtils.del(key);
}
其中EncryptUtils.desDecrypt方法:
public static String desDecrypt(String source) throws Exception {
byte[] src = hex2byte(source.getBytes());
DESKeySpec desKeySpec = getDesKeySpec(source);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
byte[] retByte = cipher.doFinal(src);
return new String(retByte);
}
AnonymousAccess 注解的使用对应Security config的抽血方法configure:
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// 搜寻匿名标记 url: @AnonymousAccess
Map handlerMethodMap = applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();
Set anonymousUrls = new HashSet<>();
for (Map.Entry infoEntry : handlerMethodMap.entrySet()) {
HandlerMethod handlerMethod = infoEntry.getValue();
AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);
if (null != anonymousAccess) {
anonymousUrls.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
}
}



