在閱讀本文之前可以先看看springsecurity的基本執行流程,下面我展示一些核心配置文件,后面給出完整的整合代碼到git上面,有興趣的小伙伴可以下載進行研究
使用maven工程構建項目,首先需要引入最核心的依賴,
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
由於這里我們整合的項目進行了前后端分離,所以我們首先需要自定義登錄成功和失敗,登出成功的自定義處理類
其實就是實現不同的handler即可:1.首先我們來看登錄成功的處理類
/**
* 處理登錄驗證成功的類
* @author zhoukebo
* @date 2018/9/4
*/
@Component
public class FuryAuthSuccessHandler implements AuthenticationSuccessHandler {
/**Json轉化工具*/
@Autowired
private ObjectMapper objectMapper;
@Override
public void onAuthenticationSuccess(HttpServletRequest request,HttpServletResponse response, Authentication authentication) throws IOException{
SysUser userDetails = (SysUser)authentication.getPrincipal();
System.out.println("管理員 " + userDetails.getUsername() + " 登錄");
Map<String,String> map=new HashMap<>(2);
map.put("code", "200");
map.put("msg", "登錄成功");
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(map));
}
}
2.登錄驗證失敗的類
/**
* 處理登錄驗證失敗的類
* @author zhoukebo
* @date 2018/9/4
*/
@Component
public class FuryAuthFailureHandler implements AuthenticationFailureHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
System.out.println("登錄驗證失敗");
Map<String,String> map=new HashMap<>(2);
map.put("code", "10001");
map.put("msg", exception.getMessage());
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(map));
}
}
3.自定義處理注銷成功的類
/**
* 處理注銷成功
* @author zhoukebo
* @date 2018/9/4
*/
@Component
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {
/**Json轉化工具*/
@Autowired
private ObjectMapper objectMapper;
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException{
Map<String,String> map=new HashMap<>(2);
map.put("code", "200");
map.put("msg", "登出成功");
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(map));
}
}
4.自定義沒有權限的處理類
/**
* 處理沒有權限的類
* @author zhoukebo
* @date 2018/9/5
*/
@Component
public class RestAuthAccessDeniedHandler implements AccessDeniedHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
Map<String,String> map=new HashMap<>(2);
map.put("code", "403");
map.put("msg", e.getMessage());
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(map));
}
}
然后對springsecurity進行詳細的配置需要繼承WebSecurityConfigurerAdapter類,下面是配置文件的詳情
/**
* spring Security配置安全控制中心
*
* @author zhoukb
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 依賴注入自定義的登錄成功處理器
*/
@Autowired
private FuryAuthSuccessHandler furyAuthSuccessHandler;
/**
* 依賴注入自定義的登錄失敗處理器
*/
@Autowired
private FuryAuthFailureHandler furyAuthFailureHandler;
/**
* 依賴注入自定義的注銷成功的處理器
*/
@Autowired
private MyLogoutSuccessHandler myLogoutSuccessHandler;
/**
* 注冊沒有權限的處理器
*/
@Autowired
private RestAuthAccessDeniedHandler restAuthAccessDeniedHandler;
/***注入自定義的CustomPermissionEvaluator*/
@Bean
public DefaultWebSecurityExpressionHandler webSecurityExpressionHandler() {
DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
handler.setPermissionEvaluator(new CustomPermissionEvaluator());
return handler;
}
/***注入我們自己的登錄邏輯驗證器AuthenticationProvider*/
@Autowired
private AuthenticationProvider authenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//這里可啟用我們自己的登陸驗證邏輯
auth.authenticationProvider(authenticationProvider);
}
/**
* 配置spring security的控制邏輯
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//"/login"不進行權限驗證
.antMatchers("/login").permitAll()
.antMatchers("/favicon.ico").permitAll()
.anyRequest().authenticated() //其他的需要登陸后才能訪問
.and()
.formLogin()
//loginProcessingUrl用於指定前后端分離的時候調用后台登錄接口的名稱
.loginProcessingUrl("/login")
//配置登錄成功的自定義處理類
.successHandler(furyAuthSuccessHandler)
//配置登錄失敗的自定義處理類
.failureHandler(furyAuthFailureHandler)
.and()
//loginProcessingUrl用於指定前后端分離的時候調用后台注銷接口的名稱
.logout().logoutUrl("/logout")
.logoutSuccessHandler(myLogoutSuccessHandler)
.and()
//配置沒有權限的自定義處理類
.exceptionHandling().accessDeniedHandler(restAuthAccessDeniedHandler)
.and()
.cors()//新加入
.and()
.csrf().disable();// 取消跨站請求偽造防護
}
}
上面我們配置了自定義的登錄邏輯的驗證MyAuthenticationProvider,和自定義的權限驗證CustomPermissionEvaluator代碼如下
/**
* 實現自己的AuthenticationProvider類,用來自定義用戶校驗機制
* @author zhoukebo
* @date 2018/9/5
*/
@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
@Autowired
private CustomerDetailService customerDetailService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 獲取表單輸入中返回的用戶名;
String userName = (String) authentication.getPrincipal();
// 獲取表單中輸入的密碼;
String password = (String) authentication.getCredentials();
// 這里調用我們的自己寫的獲取用戶的方法;
UserDetails userInfo = customerDetailService.loadUserByUsername(userName);
if (userInfo == null) {
throw new BadCredentialsException("用戶名不存在");
}
// 這里我們還要判斷密碼是否正確,這里我們的密碼使用BCryptPasswordEncoder進行加密的
if (!new BCryptPasswordEncoder().matches(password, userInfo.getPassword())) {
throw new BadCredentialsException("密碼不正確");
}
// 這里還可以加一些其他信息的判斷,比如用戶賬號已停用等判斷。
Collection<? extends GrantedAuthority> authorities = userInfo.getAuthorities();
// 構建返回的用戶登錄成功的token
return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
}
@Override
public boolean supports(Class<?> authentication) {
// 這里直接改成retrun true;表示是支持這個執行
return true;
}
}
/**
* 我們需要自定義對hasPermission()方法的處理,
* 就需要自定義PermissionEvaluator,創建類CustomPermissionEvaluator,實現PermissionEvaluator接口。
* @author zhoukebo
* @date 2018/9/5
*/
@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
/**
* 自定義驗證方法
* @param authentication 登錄的時候存儲的用戶信息
* @param targetDomainObject @PreAuthorize("hasPermission('/hello/**','r')") 中hasPermission的第一個參數
* @param permission @PreAuthorize("hasPermission('/hello/**','r')") 中hasPermission的第二個參數
* @return
*/
@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
// 獲得loadUserByUsername()方法的結果
SysUser user = (SysUser)authentication.getPrincipal();
// 獲得loadUserByUsername()中注入的權限
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
// 遍歷用戶權限進行判定
for(GrantedAuthority authority : authorities) {
UrlGrantedAuthority urlGrantedAuthority = (UrlGrantedAuthority) authority;
String permissionUrl = urlGrantedAuthority.getPermissionUrl();
// 如果訪問的Url和權限用戶符合的話,返回true
if(targetDomainObject.equals(permissionUrl)) {
return true;
}
}
return false;
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
return false;
}
}
注意:上面的自定義權限要生效還需要在WebSecurityConfig上面加上注解@EnableGlobalMethodSecurity(prePostEnabled = true)
完成登錄邏輯還需要我們實現UserDetailsService接口,以便系統能夠根據用戶名去獲取用戶的信息,里面還可加上自己的邏輯
/**
* 需要自定義UserDetailsService實現spring security的UserDetailsService接口
* @author zhoukebo
* @date 2018/9/4
*/
@Service
public class CustomerDetailService implements UserDetailsService {
@Autowired
SysUserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("用戶名不存在");
}
List<SysRole> roles = user.getRoles();
//將所有的角色對應的資源權限全部放入user對應的grantedAuthority集合中
for (SysRole role : roles) {
List<SysResource> resources = role.getResources();
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
for (SysResource resource : resources) {
if (resource != null && resource.getResourceName()!=null) {
GrantedAuthority grantedAuthority = new UrlGrantedAuthority(resource.getMethodPath(),resource.getResourceName());
grantedAuthorities.add(grantedAuthority);
}
}
user.setGrantedAuthority(grantedAuthorities);
}
System.out.println("s:" + username);
return user;
}
}
以上就完成了springboot和springsecurity的整合工作,demo中包含兩種自定義權限驗證,有興趣的小伙伴可以自行在github上面下載下來研究,不懂得可以交流,代碼有什么不妥的地方也望大家互相指教
轉自:https://www.cnblogs.com/zhoukebo/p/9674361.html
