SecurityConfig配置
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final AdminDefineHandler defineHandler;
private final UserDetailServiceImpl userDetailService;
private final AdminLoginSuccessHandler successHandler;
private final AdminLoginFailureHandler failureHandler;
private final AdminLogoutSuccessHandler logoutSuccessHandle;
private final AdminAuthenticationFilter adminAuthenticationFilter;
private final AdminAuthenticationEntryPoint authenticationEntryPoint;
private final AdminPasswordEncoder adminPasswordEncoder;
public SecurityConfig(AdminDefineHandler defineHandler, UserDetailServiceImpl userDetailService, AdminLoginSuccessHandler successHandler, AdminLoginFailureHandler failureHandler, AdminLogoutSuccessHandler logoutSuccessHandle, AdminAuthenticationFilter adminAuthenticationFilter, AdminAuthenticationEntryPoint authenticationEntryPoint, AdminPasswordEncoder adminPasswordEncoder) {
this.defineHandler = defineHandler;
this.userDetailService = userDetailService;
this.successHandler = successHandler;
this.failureHandler = failureHandler;
this.logoutSuccessHandle = logoutSuccessHandle;
this.adminAuthenticationFilter = adminAuthenticationFilter;
this.authenticationEntryPoint = authenticationEntryPoint;
this.adminPasswordEncoder = adminPasswordEncoder;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailService)
.passwordEncoder(adminPasswordEncoder);
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests()
.antMatchers("/sso/login","/swagger-resources/**","/webjars/**","/v2/**","/swagger-ui.html/**").permitAll()
.anyRequest().authenticated()
.and().formLogin().loginProcessingUrl("/sso/login")
.usernameParameter("username").passwordParameter("password")
.successHandler(successHandler)
.failureHandler(failureHandler).and()
.logout().logoutUrl("/sso/logout").clearAuthentication(true).invalidateHttpSession(true)
.logoutSuccessHandler(logoutSuccessHandle)
.and().addFilterBefore(adminAuthenticationFilter, BasicAuthenticationFilter.class)
.cors().and().csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).accessDeniedHandler(defineHandler);
}
@Override
public void configure(WebSecurity web){
web.ignoring().antMatchers("/v2/api-docs",
"/swagger-resources/configuration/ui",
"/swagger-resources",
"/swagger-resources/configuration/security",
"/swagger-ui.html");
}
实现UserDetailsService接口
@Service("userDetailService")
@Slf4j
public class UserDetailServiceImpl implements UserDetailsService {
private final SysUserMapper userMapper;
private final RouteService routeService;
private final SysUserRoleMapper userRoleMapper;
private final SysRoleMapper roleMapper;
private final SysRouteMapper routeMapper;
private final SysRoleRouteMapper roleRouteMapper;
public UserDetailServiceImpl(SysUserMapper userMapper, RouteService routeService, SysUserRoleMapper userRoleMapper, SysRoleMapper roleMapper, SysRouteMapper routeMapper, SysRoleRouteMapper roleRouteMapper, SysDepartmentMapper departmentMapper) {
this.userMapper = userMapper;
this.routeService = routeService;
this.userRoleMapper = userRoleMapper;
this.roleMapper = roleMapper;
this.routeMapper = routeMapper;
this.roleRouteMapper = roleRouteMapper;
this.departmentMapper = departmentMapper;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
wrapper.eq("username",username).eq("enable", CommonConst.NORMAL_STATE);
List<SysUser> userList = userMapper.selectList(wrapper);
if (CollectionUtils.isEmpty(userList)){
throw new UsernameNotFoundException("用户不存在");
}
SysUser sysUser = userList.get(0);
SecurityUser securityUser = new SecurityUser();
securityUser.setId(sysUser.getId());
securityUser.setUsername(sysUser.getUsername());
securityUser.setState(sysUser.getState());
securityUser.setPassword(sysUser.getPassword());
securityUser.setRoleList(getRoleList(sysUser.getId()));
securityUser.setRouteList(getRouteList(sysUser.getId()));
securityUser.setAuthorities(setAuthorityList(sysUser.getId()));
return securityUser;
}
/**
* 添加权限集合
* @param id 用户ID
* @return 权限集合
*/
private Collection<? extends GrantedAuthority> setAuthorityList(Long id) {
List<SysRoute> routeList = getRouteListByUserId(id);
Collection<GrantedAuthority> authorities = new HashSet<>();
if (!CollectionUtils.isEmpty(routeList)){
List<SysRoute> sysRoutes = routeList.stream().filter(route -> Objects.equals(route.getRouteType(), SysConst.ROUTE_PERMISSION))
.collect(Collectors.toList());
for (SysRoute sysRoute : sysRoutes) {
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(sysRoute.getPermission());
authorities.add(simpleGrantedAuthority);
}
}
return authorities;
}
/**
* 获取用户下所有的路由列表
* @param id 用户ID
* @return 路由列表
*/
private List<SysRoute> getRouteListByUserId(Long id) {
List<SysRoleVO> roleList = getRoleList(id);
if (CollectionUtils.isEmpty(roleList)){
return Collections.emptyList();
}
List<Long> roleIds = roleList.stream().map(SysRoleVO::getId).collect(Collectors.toList());
QueryWrapper<SysRoleRoute> wrapper = new QueryWrapper<>();
wrapper.in("role_id",roleIds);
List<SysRoleRoute> sysRoleRoutes = roleRouteMapper.selectList(wrapper);
if (CollectionUtils.isEmpty(sysRoleRoutes)){
return Collections.emptyList();
}
List<Long> routeIds = sysRoleRoutes.stream().map(SysRoleRoute::getRouteId).distinct().collect(Collectors.toList());
return routeMapper.selectBatchIds(routeIds);
}
/**
* 查询路由信息
* @param id 用户信息
* @return 路由列表
*/
private List<SysRouteVO> getRouteList(Long id) {
return routeService.getUserRouteList(id);
}
/**
* 添加角色信息
* @param id 用户ID
* @return 角色列表
*/
private List<SysRoleVO> getRoleList(Long id) {
QueryWrapper<SysUserRole> wrapper = new QueryWrapper<>();
wrapper.eq("user_id",id);
List<SysUserRole> userRoles = userRoleMapper.selectList(wrapper);
if (CollectionUtils.isEmpty(userRoles)){
return Collections.emptyList();
}
List<Long> roleIdList = userRoles.stream().map(SysUserRole::getRoleId).collect(Collectors.toList());
List<SysRole> sysRoles = roleMapper.selectBatchIds(roleIdList);
return sysRoles.stream().map(role -> {
SysRoleVO sysRoleVO = new SysRoleVO();
BeanUtils.copyProperties(role,sysRoleVO);
return sysRoleVO;
}).collect(Collectors.toList());
}
SecurityUser类
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties({"enabled","accountNonExpired", "accountNonLocked", "credentialsNonExpired"})
public class SecurityUser implements UserDetails {
/**
* 用户ID
*/
private Long id;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 账号禁用状态 0:启用 1 :禁用
*/
private Byte state;
/**
* 角色集合
*/
private List<SysRoleVO> roleList;
/**
* 导航栏集合
*/
private List<SysRouteVO> routeList;
/**
* token令牌
*/
private String authorization;
/**
* 权限集合
*/
@JsonDeserialize(using = AdminDeserializerUtils.class)
private Collection<? extends GrantedAuthority> authorities;
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return Objects.equals(state, SysConst.USER_ENABLE_STATE);
}
}
SecurityUser转json需要序列化
public class AdminDeserializerUtils extends JsonDeserializer<List<GrantedAuthority>> {
@Override
public List<GrantedAuthority> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
ObjectMapper mapper = (ObjectMapper) jsonParser.getCodec();
JsonNode jsonNode = mapper.readTree(jsonParser);
List<GrantedAuthority> grantedAuthorities = new LinkedList<>();
Iterator<JsonNode> elements = jsonNode.elements();
while (elements.hasNext()){
JsonNode next = elements.next();
JsonNode authority = next.get("authority");
grantedAuthorities.add(new SimpleGrantedAuthority(authority.asText()));
}
return grantedAuthorities;
}
}
每次请求接口前需要认证
@Component
@Slf4j
public class AdminAuthenticationFilter extends OncePerRequestFilter {
private final StringRedisTemplate stringRedisTemplate;
public AdminAuthenticationFilter(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = request.getHeader("Authorization");
if (StringUtils.isBlank(token)) {
filterChain.doFilter(request, response);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) throws JsonProcessingException {
String token = request.getHeader("Authorization");
String securityStr = stringRedisTemplate.opsForValue().get(RedisKeyConst.LOGIN_KEY + "::" + token);
if (securityStr != null){
ObjectMapper objectMapper = new ObjectMapper();
SecurityUser securityUser = objectMapper.readValue(securityStr, SecurityUser.class);
SecurityUserUtils.setSecurityUser(securityUser);
return new UsernamePasswordAuthenticationToken(securityUser.getUsername(),null,securityUser.getAuthorities());
}
return null;
}
}
接口认证失败
@Component
public class AdminAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException {
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
Map<String, Object> map = new HashMap<>(5);
map.put("errCode", HttpServletResponse.SC_UNAUTHORIZED);
map.put("msg", "认证失败");
map.put("data",null);
ObjectMapper om = new ObjectMapper();
out.write(om.writeValueAsString(map));
out.flush();
out.close();
}
}
权限不足
@Component
public class AdminDefineHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException{
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
Map<String, Object> map = new HashMap<>(5);
map.put("errCode", HttpServletResponse.SC_FORBIDDEN);
map.put("msg", "权限不足");
map.put("data",null);
ObjectMapper om = new ObjectMapper();
out.write(om.writeValueAsString(map));
out.flush();
out.close();
}
}
登录失败
@Component
public class AdminLoginFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException{
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
httpServletResponse.setStatus(HttpStatus.HTTP_UNAUTHORIZED);
Map<String, Object> map = new HashMap<>(5);
if (e instanceof BadCredentialsException){
map.put("errCode", ResultCodeEnum.ACCOUNT_ERROR.getCode());
map.put("msg", ResultCodeEnum.ACCOUNT_ERROR.getMsg());
} else if (e instanceof DisabledException){
map.put("errCode", ResultCodeEnum.ACCOUNT_LOCK.getCode());
map.put("msg", ResultCodeEnum.ACCOUNT_LOCK.getMsg());
}else {
map.put("errCode", ResultCodeEnum.SYSTEM_ERROR.getCode());
map.put("msg", ResultCodeEnum.SYSTEM_ERROR.getMsg());
}
map.put("data",null);
ObjectMapper mapper = new ObjectMapper();
out.write(mapper.writeValueAsString(map));
out.flush();
out.close();
}
}
登录成功
@Component
public class AdminLoginSuccessHandler implements AuthenticationSuccessHandler {
private final StringRedisTemplate stringRedisTemplate;
public AdminLoginSuccessHandler(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication auth) throws IOException {
SecurityUser securityUser = (SecurityUser) auth.getPrincipal();
securityUser.setPassword(null);
httpServletResponse.setContentType("application/json;charset=utf-8");
setAuthorization(securityUser);
//隐藏内部信息
securityUser.setDepartmentIdList(Collections.emptyList());
securityUser.setWbsList(Collections.emptyList());
securityUser.setRoleList(Collections.emptyList());
PrintWriter out = httpServletResponse.getWriter();
httpServletResponse.setStatus(HttpServletResponse.SC_OK);
Map<String, Object> map = new HashMap<>(5);
map.put("errCode", ErrCode.SUCCESS);
map.put("msg", "登录成功");
map.put("data",securityUser);
ObjectMapper om = new ObjectMapper();
out.write(om.writeValueAsString(map));
out.flush();
out.close();
}
private void setAuthorization(SecurityUser securityUser) throws JsonProcessingException {
String token = UUID.randomUUID().toString().replaceAll("-", "");
securityUser.setAuthorization(token);
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = objectMapper.writeValueAsString(securityUser);
stringRedisTemplate.opsForValue().set(RedisKeyConst.LOGIN_KEY +"::"+token,jsonStr,4, TimeUnit.HOURS);
}
}
退出登录
@Component
public class AdminLogoutSuccessHandler implements LogoutSuccessHandler {
private final StringRedisTemplate stringRedisTemplate;
public AdminLogoutSuccessHandler(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException{
String authorization = httpServletRequest.getHeader("Authorization");
stringRedisTemplate.delete(RedisKeyConst.LOGIN_KEY +"::"+authorization);
SecurityUserUtils.removeSecurityUser();
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
httpServletResponse.setStatus(HttpStatus.HTTP_OK);
Map<String, Object> map = new HashMap<>(5);
map.put("errCode", ErrCode.SUCCESS);
map.put("msg", "退出成功");
map.put("data",null);
ObjectMapper om = new ObjectMapper();
out.write(om.writeValueAsString(map));
out.flush();
out.close();
}
}
自定义密码加密解密
@Component
public class AdminPasswordEncoder implements PasswordEncoder {
/**
* 对密码加密返回
* @param charSequence 未加密字符串
* @return 加密后的字符串
*/
@SneakyThrows
@Override
public String encode(CharSequence charSequence) {
String str = charSequence.toString();
return AesSecurityUtils.encrypt(AesSecurityUtils.AES_KEY,str);
}
/**
* 验证密码是否正确
* @param charSequence 未加密字符串
* @param encodedPassword 加密后字符串
* @return 两个字符串是否相等
*/
@SneakyThrows
@Override
public boolean matches(CharSequence charSequence, String encodedPassword) {
String str = charSequence.toString();
String decrypt = AesSecurityUtils.decrypt(AesSecurityUtils.AES_KEY, encodedPassword);
return str.equals(decrypt);
}
}