日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

spring security默认登录页面登录用户,和自定义数据源

發布時間:2025/3/15 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 spring security默认登录页面登录用户,和自定义数据源 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、默認登錄頁面

  • 請求 /hello 接口,在引入 spring security 之后會先經過一些列過濾器
  • 在請求到達 FilterSecurityInterceptor時,發現請求并未認證。請求攔截下來,并拋出 AccessDeniedException 異常。
  • 拋出 AccessDeniedException 的異常會被 ExceptionTranslationFilter 捕獲,這個 Filter 中會調用 LoginUrlAuthenticationEntryPoint#commence 方法給客戶端返回302,要求客戶端進行重定向到 /login 頁面。
  • 客戶端發送 /login 請求。
  • /login 請求會再次被攔截器中 DefaultLoginPageGeneratingFilter 攔截到,并在攔截器中返回生成登錄頁面。
  • //DefaultLoginPageGeneratingFilter.generateLoginPageHtml()private String generateLoginPageHtml(HttpServletRequest request, boolean loginError, boolean logoutSuccess) {String errorMsg = "Invalid credentials";if (loginError) {HttpSession session = request.getSession(false);if (session != null) {AuthenticationException ex = (AuthenticationException)session.getAttribute("SPRING_SECURITY_LAST_EXCEPTION");errorMsg = ex != null ? ex.getMessage() : "Invalid credentials";}}String contextPath = request.getContextPath();StringBuilder sb = new StringBuilder();sb.append("<!DOCTYPE html>\n");sb.append("<html lang=\"en\">\n");sb.append(" <head>\n");sb.append(" <meta charset=\"utf-8\">\n");sb.append(" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n");sb.append(" <meta name=\"description\" content=\"\">\n");sb.append(" <meta name=\"author\" content=\"\">\n");sb.append(" <title>Please sign in</title>\n");sb.append(" <link href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css\" rel=\"stylesheet\" integrity=\"sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M\" crossorigin=\"anonymous\">\n");sb.append(" <link href=\"https://getbootstrap.com/docs/4.0/examples/signin/signin.css\" rel=\"stylesheet\" crossorigin=\"anonymous\"/>\n");sb.append(" </head>\n");sb.append(" <body>\n");sb.append(" <div class=\"container\">\n");if (this.formLoginEnabled) {sb.append(" <form class=\"form-signin\" method=\"post\" action=\"" + contextPath + this.authenticationUrl + "\">\n");sb.append(" <h2 class=\"form-signin-heading\">Please sign in</h2>\n");sb.append(createError(loginError, errorMsg) + createLogoutSuccess(logoutSuccess) + " <p>\n");sb.append(" <label for=\"username\" class=\"sr-only\">Username</label>\n");sb.append(" <input type=\"text\" id=\"username\" name=\"" + this.usernameParameter + "\" class=\"form-control\" placeholder=\"Username\" required autofocus>\n");sb.append(" </p>\n");sb.append(" <p>\n");sb.append(" <label for=\"password\" class=\"sr-only\">Password</label>\n");sb.append(" <input type=\"password\" id=\"password\" name=\"" + this.passwordParameter + "\" class=\"form-control\" placeholder=\"Password\" required>\n");sb.append(" </p>\n");sb.append(this.createRememberMe(this.rememberMeParameter) + this.renderHiddenInputs(request));sb.append(" <button class=\"btn btn-lg btn-primary btn-block\" type=\"submit\">Sign in</button>\n");sb.append(" </form>\n");}if (this.openIdEnabled) {sb.append(" <form name=\"oidf\" class=\"form-signin\" method=\"post\" action=\"" + contextPath + this.openIDauthenticationUrl + "\">\n");sb.append(" <h2 class=\"form-signin-heading\">Login with OpenID Identity</h2>\n");sb.append(createError(loginError, errorMsg) + createLogoutSuccess(logoutSuccess) + " <p>\n");sb.append(" <label for=\"username\" class=\"sr-only\">Identity</label>\n");sb.append(" <input type=\"text\" id=\"username\" name=\"" + this.openIDusernameParameter + "\" class=\"form-control\" placeholder=\"Username\" required autofocus>\n");sb.append(" </p>\n");sb.append(this.createRememberMe(this.openIDrememberMeParameter) + this.renderHiddenInputs(request));sb.append(" <button class=\"btn btn-lg btn-primary btn-block\" type=\"submit\">Sign in</button>\n");sb.append(" </form>\n");}Iterator var7;Entry relyingPartyUrlToName;String url;String partyName;if (this.oauth2LoginEnabled) {sb.append("<h2 class=\"form-signin-heading\">Login with OAuth 2.0</h2>");sb.append(createError(loginError, errorMsg));sb.append(createLogoutSuccess(logoutSuccess));sb.append("<table class=\"table table-striped\">\n");var7 = this.oauth2AuthenticationUrlToClientName.entrySet().iterator();while(var7.hasNext()) {relyingPartyUrlToName = (Entry)var7.next();sb.append(" <tr><td>");url = (String)relyingPartyUrlToName.getKey();sb.append("<a href=\"").append(contextPath).append(url).append("\">");partyName = HtmlUtils.htmlEscape((String)relyingPartyUrlToName.getValue());sb.append(partyName);sb.append("</a>");sb.append("</td></tr>\n");}sb.append("</table>\n");}if (this.saml2LoginEnabled) {sb.append("<h2 class=\"form-signin-heading\">Login with SAML 2.0</h2>");sb.append(createError(loginError, errorMsg));sb.append(createLogoutSuccess(logoutSuccess));sb.append("<table class=\"table table-striped\">\n");var7 = this.saml2AuthenticationUrlToProviderName.entrySet().iterator();while(var7.hasNext()) {relyingPartyUrlToName = (Entry)var7.next();sb.append(" <tr><td>");url = (String)relyingPartyUrlToName.getKey();sb.append("<a href=\"").append(contextPath).append(url).append("\">");partyName = HtmlUtils.htmlEscape((String)relyingPartyUrlToName.getValue());sb.append(partyName);sb.append("</a>");sb.append("</td></tr>\n");}sb.append("</table>\n");}sb.append("</div>\n");sb.append("</body></html>");return sb.toString();}private String renderHiddenInputs(HttpServletRequest request) {StringBuilder sb = new StringBuilder();Iterator var3 = ((Map)this.resolveHiddenInputs.apply(request)).entrySet().iterator();while(var3.hasNext()) {Entry<String, String> input = (Entry)var3.next();sb.append("<input name=\"");sb.append((String)input.getKey());sb.append("\" type=\"hidden\" value=\"");sb.append((String)input.getValue());sb.append("\" />\n");}return sb.toString();}

    就是通過這種方式,Spring Security 默認過濾器中生成了登錄頁面,并返回!

    二、默認登錄用戶

  • 查看 SpringBootWebSecurityConfiguration.defaultSecurityFilterChain 方法表單登錄
  • 處理登錄為 FormLoginConfigurer 類中 調用 這個類實例
  • 查看類中 UsernamePasswordAuthenticationFilter#attempAuthentication 方法得知實際調用 AuthenticationManager 中 authenticate 方法
  • 調用 ProviderManager 類中方法 authenticate
  • 調用了 ProviderManager 實現類中 AbstractUserDetailsAuthenticationProvider類中方法
  • 最終調用實現類 DaoAuthenticationProvider 類中方法比較
  • 看到這里就知道默認實現是基于 InMemoryUserDetailsManager 這個類,也就是內存的實現!

    三、自定義用戶數據源

  • InMemoryUserDetailsManager
  • 看一下InMemoryUserDetailsManager的繼承圖


    可以看到頂層是UserDetailService接口,接口中 loadUserByUserName 方法是用來在認證時進行用戶名認證方法,默認實現使用是內存實現,如果想要修改數據庫實現我們只需要自定義 UserDetailService 實現,最終返回 UserDetails 實例即可。

    public interface UserDetailsService {UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; }

    在第一篇文章中,我們知道,springboot會基于SPI機制,導入一個UserDetailServiceAutoConfigutation,用于對UserDetailService進行配置。

    @Configuration(proxyBeanMethods = false ) @ConditionalOnClass({AuthenticationManager.class}) @ConditionalOnBean({ObjectPostProcessor.class}) @ConditionalOnMissingBean(value = {AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class, AuthenticationManagerResolver.class},type = {"org.springframework.security.oauth2.jwt.JwtDecoder", "org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector", "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository"} ) public class UserDetailsServiceAutoConfiguration {private static final String NOOP_PASSWORD_PREFIX = "{noop}";private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern.compile("^\\{.+}.*$");private static final Log logger = LogFactory.getLog(UserDetailsServiceAutoConfiguration.class);public UserDetailsServiceAutoConfiguration() {}@Bean@Lazy//默認是InMemoryUserDetailsManager public InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties, ObjectProvider<PasswordEncoder> passwordEncoder) {User user = properties.getUser();List<String> roles = user.getRoles();return new InMemoryUserDetailsManager(new UserDetails[]{org.springframework.security.core.userdetails.User.withUsername(user.getName()).password(this.getOrDeducePassword(user, (PasswordEncoder)passwordEncoder.getIfAvailable())).roles(StringUtils.toStringArray(roles)).build()});}private String getOrDeducePassword(User user, PasswordEncoder encoder) {String password = user.getPassword();if (user.isPasswordGenerated()) {logger.info(String.format("%n%nUsing generated security password: %s%n", user.getPassword()));}return encoder == null && !PASSWORD_ALGORITHM_PATTERN.matcher(password).matches() ? "{noop}" + password : password;} }
    • 結論
  • 從自動配置源碼中得知當 classpath 下存在 AuthenticationManager 類
  • 當前項目中,系統沒有提供 AuthenticationManager.class、 AuthenticationProvider.class、UserDetailsService.class、 AuthenticationManagerResolver.class、實例
  • 默認情況下都會滿足,此時Spring Security會提供一個 InMemoryUserDetailManager 實例

    @ConfigurationProperties(prefix = "spring.security") public class SecurityProperties {private final User user = new User();public User getUser() {return this.user;}//....public static class User {private String name = "user";private String password = UUID.randomUUID().toString();private List<String> roles = new ArrayList<>();private boolean passwordGenerated = true;//get set ...} }

    這就是默認生成 user 以及 uuid 密碼過程! 另外看明白源碼之后,就知道只要在配置文件中加入如下配置可以對內存中用戶和密碼進行覆蓋。

    spring.security.user.name=root spring.security.user.password=root spring.security.user.roles=admin,users
  • 自定義數據源
    • 自定義內存數據源:沒什么意義
    @Configuration public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {@Beanpublic UserDetailsService userDetailsService(){InMemoryUserDetailsManager inMemoryUserDetailsManager= new InMemoryUserDetailsManager();UserDetails u1 = User.withUsername("zhangs").password("{noop}111").roles("USER").build();inMemoryUserDetailsManager.createUser(u1);return inMemoryUserDetailsManager;}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService());} }
    • 自定義數據庫數據源

    對象實現UserDetails 接口

    public class User implements UserDetails {private Integer id;private String username;private String password;private Boolean enabled;private Boolean accountNonExpired;private Boolean accountNonLocked;private Boolean credentialsNonExpired;private List<Role> roles = new ArrayList<>();@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {List<GrantedAuthority> grantedAuthorities = new ArrayList<>();roles.forEach(role->grantedAuthorities.add(new SimpleGrantedAuthority(role.getName())));return grantedAuthorities;}@Overridepublic String getPassword() {return password;}@Overridepublic String getUsername() {return username;}@Overridepublic boolean isAccountNonExpired() {return accountNonExpired;}@Overridepublic boolean isAccountNonLocked() {return accountNonLocked;}@Overridepublic boolean isCredentialsNonExpired() {return credentialsNonExpired;}@Overridepublic boolean isEnabled() {return enabled;}//get/set.... }

    Role 類

    public class Role {private Integer id;private String name;private String nameZh;//get set.. }

    創建 UserDao 接口

    @Mapper public interface UserDao {//根據用戶名查詢用戶User loadUserByUsername(String username);//根據用戶id查詢角色List<Role> getRolesByUid(Integer uid); } 創建 UserDetailService 實例 @Component public class MyUserDetailService implements UserDetailsService {private final UserDao userDao;@Autowiredpublic MyUserDetailService(UserDao userDao) {this.userDao = userDao;}@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = userDao.loadUserByUsername(username);if(ObjectUtils.isEmpty(user))throw new RuntimeException("用戶不存在");user.setRoles(userDao.getRolesByUid(user.getId()));return user;} }

    配置 authenticationManager 使用自定義UserDetailService

    @Configuration public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {private final UserDetailsService userDetailsService;@Autowiredpublic WebSecurityConfigurer(UserDetailsService userDetailsService) {this.userDetailsService = userDetailsService;}@Overrideprotected void configure(AuthenticationManagerBuilder builder) throws Exception {builder.userDetailsService(userDetailsService);}@Overrideprotected void configure(HttpSecurity http) throws Exception {//web security..} }

    數據庫表和mapper.xml按要求設計即可

    總結

    以上是生活随笔為你收集整理的spring security默认登录页面登录用户,和自定义数据源的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。