spring-boot: 2.1.4.RELEASE
spring-security-oauth3: 2.3.3.RELEASE (Jika anda ingin menggunakan kod sumber, jangan ubah ini nombor versi sesuka hati, kerana 2.4 ditulis secara berbeza)
mysql: 5.7
Hanya posmen digunakan untuk ujian di sini, dan halaman hadapan tidak digunakan untuk dok lagi. Seterusnya Akan ada paparan halaman untuk peruntukan kebenaran menu peranan setiap versi
1 Akses antara muka terbuka http://localhost:7000/open/hello
2 , Akses antara muka yang dilindungi http://localhost:7000/admin/user/info
tanpa token Log masuk semasa maklumat pengguna
oauth3 mempunyai empat mod secara keseluruhan, yang tidak akan dijelaskan di sini mencari dalam talian dan menemui perkara yang sama
Kerana sekarang kami hanya mempertimbangkan aplikasi unilateral, jadi mod kata laluan digunakan.
Akan ada artikel di SpringCloud+Oauth3 nanti, pengesahan get laluan
Mari kita bincangkan beberapa perkara
1 keizinan dinamik konfigurasi pemintas
Buat kelas MySecurityFilter baharu, warisi AbstractSecurityInterceptor dan laksanakan antara muka Penapis
Permulaan, sesuaikan pengurus keputusan akses
@PostConstruct public void init(){ super.setAuthenticationManager(authenticationManager); super.setAccessDecisionManager(myAccessDecisionManager); }
Penapis tersuai memanggil elemen keselamatan Data sumber
@Override public SecurityMetadataSource obtainSecurityMetadataSource() { return this.mySecurityMetadataSource; }
Mari kita lihat dahulu kod teras penapis tersuai yang memanggil sumber metadata selamat
Kod berikut digunakan untuk mendapatkan kebenaran (peranan) yang diperlukan untuk permintaan semasa untuk masuk
/** * 获得当前请求所需要的角色 * @param object * @return * @throws IllegalArgumentException */ @Override public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { String requestUrl = ((FilterInvocation) object).getRequestUrl(); if (IS_CHANGE_SECURITY) { loadResourceDefine(); } if (requestUrl.indexOf("?") > -1) { requestUrl = requestUrl.substring(0, requestUrl.indexOf("?")); } UrlPathMatcher matcher = new UrlPathMatcher(); List<Object> list = new ArrayList<>(); //无需权限的,直接返回 list.add("/oauth/**"); list.add("/open/**"); if(matcher.pathsMatchesUrl(list,requestUrl)) return null; Set<String> roleNames = new HashSet(); for (Resc resc: resources) { String rescUrl = resc.getResc_url(); if (matcher.pathMatchesUrl(rescUrl, requestUrl)) { if(resc.getParent_resc_id() != null && resc.getParent_resc_id().intValue() == 1){ //默认权限的则只要登录了,无需权限匹配都可访问 roleNames = new HashSet(); break; } Map map = new HashMap(); map.put("resc_id", resc.getResc_id()); // 获取能访问该资源的所有权限(角色) List<RoleRescDTO> roles = roleRescMapper.findAll(map); for (RoleRescDTO rr : roles) roleNames.add(rr.getRole_name()); } } Set<ConfigAttribute> configAttributes = new HashSet(); for(String roleName:roleNames) configAttributes.add(new SecurityConfig(roleName)); log.debug("【所需的权限(角色)】:" + configAttributes); return configAttributes; }
Mari kita lihat kod teras pengurus keputusan akses tersuai Kod ini digunakan terutamanya untuk menentukan sama ada pengguna yang sedang log masuk (peranan yang dimiliki oleh pengguna yang sedang log masuk akan ditulis. dalam item terakhir) mempunyai peranan kebenaran
@Override public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { if(configAttributes == null){ //属于白名单的,不需要权限 return; } Iterator<ConfigAttribute> iterator = configAttributes.iterator(); while (iterator.hasNext()){ ConfigAttribute configAttribute = iterator.next(); String needPermission = configAttribute.getAttribute(); for (GrantedAuthority ga: authentication.getAuthorities()) { if(needPermission.equals(ga.getAuthority())){ //有权限,可访问 return; } } } throw new AccessDeniedException("没有权限访问"); }
2 Pengecualian pengesahan tersuai mengembalikan hasil yang sama
Mengapa ini diperlukan? hujung dan belakang untuk memahami kandungan yang dikembalikan apabila pengesahan gagal Ia tidak boleh ditafsirkan secara seragam, jadi tanpa berlengah lagi, mari kita lihat situasi pemulangan tanpa konfigurasi dan konfigurasi
(1) Sebelum penyesuaian, apabila. anda tidak membawa token untuk mengakses antara muka API yang dilindungi, hasil yang dikembalikan adalah Seperti ini
(2) Mari kita tetapkan bahawa selepas antara muka yang gagal pengesahan kembali ke antara muka , ia akan menjadi yang berikut. Adakah lebih baik untuk kita memproses dan menggesa pengguna
Baiklah, mari kita lihat di mana untuk mengkonfigurasinya Pelayan sumber kami OautyResourceConfig, tulis semula bahagian kod berikut untuk menyesuaikan pengesahan Hasilnya dikembalikan oleh kanan abnormal
Anda boleh merujuk kepada ini https://www.yisu.com/article/131668.htm
@Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.authenticationEntryPoint(authenticationEntryPoint) //token失效或没携带token时 .accessDeniedHandler(requestAccessDeniedHandler); //权限不足时 }
3. Dapatkan pengguna log masuk Semasa
Yang pertama: gunakan JWT untuk membawa maklumat pengguna, dan kemudian menghuraikannya selepas mendapat token
Tiada penjelasan buat masa ini
Yang kedua: tulis SecurityUser untuk melaksanakan UserDetails Interface (ini adalah yang digunakan dalam projek ini)
Antara muka UserDetails yang asal hanya mempunyai nama pengguna dan kata laluan . Di sini kami menambah Pengguna dalam sistem kami
protected User user; public SecurityUser(User user) { this.user = user; } public User getUser() { return user; }
Dalam BaseController, setiap Pengawal Semua akan mewarisi ini, dan menulis kaedah getUser() di dalamnya selagi pengguna membawa token untuk mengakses, kami boleh terus dapatkan maklumat pengguna yang sedang log masuk
protected User getUser() { try { SecurityUser userDetails = (SecurityUser) SecurityContextHolder.getContext().getAuthentication() .getPrincipal(); User user = userDetails.getUser(); log.debug("【用户:】:" + user); return user; } catch (Exception e) { } return null; }
Jadi bagaimana untuk mendapatkannya selepas pengguna berjaya log masuk Bagi koleksi peranan pengguna, dsb., antara muka UserDetailsService mesti dilaksanakan di sini
<🎜? >@Service public class TokenUserDetailsService implements UserDetailsService{ @Autowired private LoginService loginService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = loginService.loadUserByUsername(username); //这个我们拎出来处理 if(Objects.isNull(user)) throw new UsernameNotFoundException("用户名不存在"); return new SecurityUser(user); } }
Kemudian dalam kelas konfigurasi keselamatan kami, tetapkan UserDetailsService kepada yang kami tulis di atas
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); }
Akhirnya kami hanya perlu melaksanakan kami kaedah dalam loginService, dan nilai sama ada pengguna itu wujud berdasarkan pemprosesan perniagaan sebenar kami
@Override public User loadUserByUsername(String username){ log.debug(username); Map map = new HashMap(); map.put("username",username); map.put("is_deleted",-1); User user = userMapper.findByUsername(map); if(user != null){ map = new HashMap(); map.put("user_id",user.getUser_id()); //查询用户的角色 List<UserRoleDTO> userRoles = userRoleMapper.findAll(map); user.setRoles(listRoles(userRoles)); //权限集合 Collection<? extends GrantedAuthority> authorities = merge(userRoles); user.setAuthorities(authorities); return user; } return null; }
Atas ialah kandungan terperinci Bagaimanakah SpringBoot mengintegrasikan SpringSecurityOauth2 untuk melaksanakan isu kebenaran dinamik untuk pengesahan?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!