引入依赖
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency>
|
SpringSecurity 配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
| @EqualsAndHashCode(callSuper = true) @Data @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true) @ConfigurationProperties(prefix = "spring.security.ignore") public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private List<String[]> marchers;
@Bean public UserService userService(){ return new UserService(); }
@Override protected void configure(HttpSecurity http) throws Exception { http .anonymous().disable() .csrf().disable() .authorizeRequests()
.anyRequest().authenticated() .and() .formLogin()
.loginProcessingUrl("/login") .successHandler(authenticationSuccessHandler()) .failureHandler(authenticationFailureHandler()) .permitAll() .and() .logout() .invalidateHttpSession(true) .deleteCookies("JSESSIONID") .logoutSuccessHandler(new LogoutSuccess()) .permitAll() .and() .sessionManagement() .invalidSessionStrategy(new InvalidSessionStrategyImpl()) .maximumSessions(-1).expiredSessionStrategy(expiredSessionStrategy()) .sessionRegistry(sessionRegistry()) .and() .and() .exceptionHandling() .authenticationEntryPoint(new UnauthenticatedEntryPoint()) .accessDeniedHandler(new AuthorizationFailure()) .and() .addFilterBefore(new AuthorizationFilter(new AuthorizationMetadataSource(), new AuthorizationAccessDecisionManager()), FilterSecurityInterceptor.class);
}
@Override public void configure(AuthenticationManagerBuilder auth) { auth.authenticationProvider(authenticationProvider()); }
@Bean public SessionRegistry sessionRegistry(){ return new SessionRegistryImpl(); }
@Bean public ExpiredSessionStrategyImpl expiredSessionStrategy(){ return new ExpiredSessionStrategyImpl(); }
@Bean public BCryptPasswordEncoder passwordEncoder() { return SecurityUtils.getPasswordEncoder(); }
@Override public void configure(WebSecurity web) { for (String[] marcher : marchers) { web.ignoring().antMatchers(marcher); } }
@Bean public DaoAuthenticationProvider authenticationProvider() { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setHideUserNotFoundExceptions(false); provider.setUserDetailsService(userService()); provider.setPasswordEncoder(passwordEncoder()); return provider; }
@Bean public AuthenticationSuccess authenticationSuccessHandler(){ return new AuthenticationSuccess(); }
@Bean public AuthenticationFailureHandler authenticationFailureHandler(){ return new AuthenticationFailure(); } }
|
自定义 userService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| public class UserService implements UserDetailsService { @Autowired private WebSecurityConfig securityConfig;
@Autowired private SysUserMapper userMapper;
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { if (StringUtils.isEmpty(username)) { throw new UsernameNotFoundException("用户名不能为空!"); } SysUser user = new SysUser(); user.setLoginName(username); SysUser queryUser = userMapper.selectOne(user); if (null == queryUser) { throw new UsernameNotFoundException("用户 " + username + " 不存在!"); } if (!queryUser.getPermissionIpList().contains("0.0.0.0") && !queryUser.getPermissionIpList().contains (SecurityUtils.getRemoteAddress())) { throw new InvalidIpAddrException("登录 IP 地址不合法"); }
return new SecurityUser(queryUser); }
public void reAuthorization(){ SecurityUser user = SecurityUtils.currentUser(); assert user != null; String username = user.getUsername(); user.setRoles(userMapper.findRolesByName(username)); user.setMenus(userMapper.findMenusByName(username)); user.setFunctions(userMapper.findFunctionsByName(username));
List<GrantedAuthority> authorities = new ArrayList<>(); for (Function function : user.getFunctions()) { for (String url : function.getFunctionUrl().split(",")) { authorities.add(new SimpleGrantedAuthority(url)); } } user.setAuthorities(authorities.stream().distinct().collect(Collectors.toList())); Authentication auth = SecurityUtils.getAuthentication(); Authentication newAuth = new UsernamePasswordAuthenticationToken(auth.getPrincipal(), auth.getCredentials(), authorities); SecurityContextHolder.getContext().setAuthentication(newAuth);
}
public void kickOutUser(String... userNames) { SessionRegistry sessionRegistry = securityConfig.sessionRegistry(); for (Object o : sessionRegistry.getAllPrincipals()) { SecurityUser user = (SecurityUser) o; for (String username : userNames) { if (user.getLoginName().equals(username)) { for (SessionInformation sessionInformation : sessionRegistry.getAllSessions(user, false)) { sessionInformation.expireNow(); } } } } } }
|
用户实体类 SecurityUser
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| @Data public class SecurityUser extends SysUser implements UserDetails{
private List<SysRole> roles;
private List<Menu> menus;
private List<Function> functions;
private Collection<? extends GrantedAuthority> authorities;
SecurityUser(SysUser user) { this.setUserId(user.getUserId()); this.setGlbm(user.getGlbm()); this.setXh(user.getXh()); this.setLoginName(user.getLoginName()); this.setLoginPassword(user.getLoginPassword()); this.setPermissionIpList(user.getPermissionIpList()); this.setLatestLoginTime(user.getLatestLoginTime()); this.setTotalLoginCounts(user.getTotalLoginCounts()); this.setName(user.getName()); this.setCreateTime(user.getCreateTime()); this.setUpdateTime(user.getUpdateTime()); }
@Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; }
@Override public boolean isAccountNonExpired() { return true; }
@Override public boolean isAccountNonLocked() { return true; }
@Override public boolean isCredentialsNonExpired() { return true; }
@Override public boolean isEnabled() { return true; }
@Override public String getPassword() { return super.getLoginPassword(); }
@Override public String getUsername() { return super.getLoginName(); }
@Override public int hashCode() { return this.getLoginName().hashCode(); }
@Override public boolean equals(Object obj) { return obj instanceof SecurityUser && ((SecurityUser) obj).getLoginName().equals(this.getLoginName()); } }
|
授权Filter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public class AuthorizationFilter extends AbstractSecurityInterceptor implements Filter {
private AuthorizationMetadataSource metadataSource;
public AuthorizationFilter(AuthorizationMetadataSource metadataSource, AuthorizationAccessDecisionManager accessDecisionManager) { this.metadataSource = metadataSource; this.setAccessDecisionManager(accessDecisionManager); }
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain); invoke(fi); }
private void invoke(FilterInvocation fi) throws IOException, ServletException { InterceptorStatusToken token = super.beforeInvocation(fi); try { fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); } finally { super.afterInvocation(token, null); } }
@Override public Class<?> getSecureObjectClass() { return FilterInvocation.class; }
@Override public SecurityMetadataSource obtainSecurityMetadataSource() { return metadataSource; }
@Override public void destroy() { }
@Override public void init(FilterConfig filterConfig) { } }
|
授权访问决策器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| public class AuthorizationAccessDecisionManager implements AccessDecisionManager {
@Override public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { HttpServletRequest request = ((FilterInvocation) object).getRequest(); String url = ((FilterInvocation) object).getRequestUrl(); for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) { SimpleGrantedAuthority authority = (SimpleGrantedAuthority) grantedAuthority; if (matches(authority.getAuthority(), request)) { return; } } throw new AccessDeniedException("uri: " + url + ",无权限访问!"); }
@Override public boolean supports(ConfigAttribute attribute) { return true; }
@Override public boolean supports(Class<?> clazz) { return true; }
private boolean matches(String url, HttpServletRequest request) { AntPathRequestMatcher matcher = new AntPathRequestMatcher(url); return matcher.matches(request); } }
|
授权元数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class AuthorizationMetadataSource implements FilterInvocationSecurityMetadataSource {
@Override public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { String url = ((FilterInvocation) object).getRequestUrl(); Collection<ConfigAttribute> configAttributes = new ArrayList<>(); configAttributes.add(new SecurityConfig(url)); return configAttributes; }
@Override public Collection<ConfigAttribute> getAllConfigAttributes() { return null; }
@Override public boolean supports(Class<?> clazz) { return true; } }
|
封装一些 Security 工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| public class SecurityUtils {
public static Authentication getAuthentication(){ return SecurityContextHolder.getContext().getAuthentication(); }
public static boolean isAuthenticated(){ return getAuthentication() != null || !(getAuthentication() instanceof AnonymousAuthenticationToken); }
public static SecurityUser currentUser(){ if (isAuthenticated()) { return (SecurityUser) getAuthentication().getPrincipal(); } return null; }
private static WebAuthenticationDetails webAuthenticationDetails(){ return (WebAuthenticationDetails)getAuthentication().getDetails(); }
public static String getSessionId(){ return webAuthenticationDetails().getSessionId(); }
public static String getRemoteAddress(){ return webAuthenticationDetails().getRemoteAddress(); }
public static BCryptPasswordEncoder getPasswordEncoder(){ return new BCryptPasswordEncoder(4); }
public static String createPassword(String rawPassword){ return getPasswordEncoder().encode(rawPassword.trim()); }
public static boolean isMatching(String rawPassword,String encodedPassword){ return getPasswordEncoder().matches(rawPassword,encodedPassword); }
}
|
主要的实现类都列举在内了,还有一些成功和失败的处理类,再次没有列举出来
因为该项目为构建纯restful风格的后台项目,这些成功或失败的处理类基本都是返回的http状态码