SpringBoot與SpringSecurity整合的初使用
阿新 • • 發佈:2019-03-04
ans manage arr ESS ini oca ngs bcrypt meta
SpringBoot與SpringSecurity整合的初使用
1.背景:
最近的一個項目中,需要做web頁面的管理後臺,需要對不同角色進行不同的管理,特此研究了一下SpringSecurity的使用。
2.正題:
1.采用框架:springboot,springsecurity,jpa
2.采用idea的spring initializr 初始化一個springboot項目,勾選模塊為web,securiy,和jpa,jdbc。
3.表結構:
總共有三張表,分別為user,user_authorities,authentication
user表
authentication表
user_authorities表:
4.代碼開始:
4.1 首先,創建jpa對應的實體類:
這個就不貼代碼了,根據上面的表結構創建對應的類,加上jpa對應的註解即可
4.2 創建Repository類,繼承JpaRepository
4.3 創建SpringSecurity配置類WebSecurityConfigurerAdapter:
@EnableWebSecurity public class MySecurityConfig extendsWebSecurityConfigurerAdapter { @Autowired private MyUserDetailService userDetailsService; @Autowired private AuthenticationRepository authenticationRepository; @Override protected void configure(HttpSecurity http) throws Exception { //super.configure(http); http.csrf().disable();//定義請求規則,查詢數據庫,將authentication表中的請求規則查詢配置 List<Authentication> authentications = authenticationRepository.findAll(); http.authorizeRequests() .antMatchers("/").permitAll(); for(Authentication authentication :authentications){ http.authorizeRequests(). antMatchers(authentication.getDescription()) .hasRole(authentication.getName(). substring(authentication.getName().indexOf("ROLE_")+5)); } //開啟自動配置的登錄功能 http.formLogin(); //1./login請求來到登陸頁面 //2.重定向到/login?error 表示登錄失敗 //3. //開啟自動註銷功能 //訪問logout表示註銷,清空session //註銷成功會默認返回/login?logout頁面 //可以通過配置logoutSuccessUrl來配置註銷成功的返回地址 http.logout().logoutSuccessUrl("/"); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(authenticationProvider()); } public DaoAuthenticationProvider authenticationProvider(){ DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); authenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder(8)); authenticationProvider.setUserDetailsService(userDetailsService); return authenticationProvider; } }
4.4 配置 AccessDecisionManager類
@Service public class MyAccessDecisionManager implements AccessDecisionManager { public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { if (configAttributes == null ) { throw new AccessDeniedException("對不起,您沒有此權限"); } SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println(sdf.format(new Date())+":\t"+object.toString()); System.out.println("configAttributes=="+configAttributes); System.out.println("authentication=="+authentication.getAuthorities()); for(ConfigAttribute ca:configAttributes){ String needRole = ca.getAttribute(); for(GrantedAuthority userGA:authentication.getAuthorities()) { if(needRole.equals(userGA.getAuthority())) { // ga is user‘s role. return ; } } } throw new AccessDeniedException("對不起,您沒有此權限"); } public boolean supports(ConfigAttribute arg0) { // TODO Auto-generated method stub return true; } public boolean supports(Class<?> arg0) { // TODO Auto-generated method stub return true; } }
4.5 實現一個繼承userDetailService的類,根據業務邏輯實現loadUserByUserName
@Service public class MyUserDetailService implements UserDetailsService { @Autowired private UsersRepository userRepository; @Autowired private UserAuthoritiesRepository userAuthoritiesRepository; @Override @Transactional public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { User user = userRepository.findByUsername(s); if(user == null){ throw new UsernameNotFoundException(s); } List<GrantedAuthority> authorities = new ArrayList<>(); List<UserAuthorities> userAuthorities = userAuthoritiesRepository.findAllByUser(user); for(UserAuthorities userAuthorities1:userAuthorities){ authorities.add(new SimpleGrantedAuthority(userAuthorities1.getAuthentication().getName())); } return new MyUserPrincipal(user,authorities); } }
4.6 實現AbstractSecurityInterceptor,繼承Filter
@Service public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter { @Autowired private FilterInvocationSecurityMetadataSource securityMetadataSource; @Autowired public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) { super.setAccessDecisionManager(myAccessDecisionManager); } public FilterInvocationSecurityMetadataSource getSecurityMetadataSource(){ return this.securityMetadataSource; } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { FilterInvocation fi = new FilterInvocation(request, response, chain); invoke(fi); } public void invoke(FilterInvocation fi) throws IOException, ServletException { //fi裏面有一個被攔截的url //裏面調用MyInvocationSecurityMetadataSource的getAttributes(Object object)這個方法獲取fi對應的所有權限 //再調用MyAccessDecisionManager的decide方法來校驗用戶的權限是否足夠 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 this.securityMetadataSource; } }
4.7 實現FilterInvocationSecurityMetadataSource
@Service public class MyInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource { private HashMap<String, Collection<ConfigAttribute>> map = null; @Autowired private AuthenticationRepository authenticationRepository; /** * 加載權限表中所有權限,這裏不想從數據庫中獲取直接寫在了這 */ public void loadResourceDefine() { map = new HashMap<>(); Collection<ConfigAttribute> array; // ConfigAttribute cfg, cfg1,cfg2; array = new ArrayList<>(); List<Authentication> authentications = authenticationRepository.findAll(); for(Authentication authentication:authentications){ System.out.println(authentication); ConfigAttribute cfg = new SecurityConfig(authentication.getName()); array.add(cfg); map.put(authentication.getDescription(),array); } } //此方法是為了判定用戶請求的url 是否在權限表中,如果在權限表中,則返回給 decide 方法, // 用來判定用戶是否有此權限。如果不在權限表中則放行。 @Override public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { System.out.println("object的類型為:" + object.getClass()); FilterInvocation filterInvocation = (FilterInvocation) object; String url = filterInvocation.getRequestUrl(); System.out.println("訪問的URL地址為(包括參數):" + url); url = filterInvocation.getRequest().getServletPath(); System.out.println("訪問的URL地址為:" + url); if (map == null) loadResourceDefine(); //object 中包含用戶請求的request 信息 final HttpServletRequest request = ((FilterInvocation) object).getHttpRequest(); AntPathRequestMatcher matcher; String resUrl; for (Iterator<String> iter = map.keySet().iterator(); iter.hasNext(); ) { resUrl = iter.next(); matcher = new AntPathRequestMatcher(resUrl); //matches() 方法用於檢測字符串是否匹配給定的正則表達式 boolean a = matcher.matches(request); if (matcher.matches(request)) { Collection<ConfigAttribute> c = map.get(resUrl); return map.get(resUrl); } } return null; // return collection; } @Override public Collection<ConfigAttribute> getAllConfigAttributes() { return null; } @Override public boolean supports(Class<?> clazz) { //UsernamePasswordAuthenticationToken.class.equals(clazz); return FilterInvocation.class.isAssignableFrom(clazz); //return true; } }
SpringBoot與SpringSecurity整合的初使用