spring-security實戰


項目架構:spring-boot+spring-security+thymeleaf

1.導包(bootthymeleaf的包請自行導入,版本號我抽的properties)

 

<dependency>
         <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
</dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
 </dependency>

 

2.核心配置類(完整代碼最后放出)

2.1自定義security的配置類,然后繼承WebSecurityConfigurerAdapter這個抽象類。

2.2打上@EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true)這兩個注解

2.3重寫configure(HttpSecurity http)方法,使用鏈式編程使用http.formLogin()方法設置你需要的配置(攔截參數,登錄路徑,跨域請求等)

2.4重寫configure(WebSecurity web)方法,釋放靜態資源

 

web.ignoring().antMatchers("/**/*.js", "/**/*.css", "/**/*.png", "/**/*.jpg", "/**/*.gif", "/**/*.map");

 

2.5重寫configure(AuthenticationManagerBuilder auth)方法,自定義你的登錄驗證

loginAuthenticationProvider:這是自定義的登錄驗證類,注入調用
customUserDetailsService:這是自定義的賬號存儲類,注入調用
customPasswordEncoder:這是自定義的密碼加密類,注入調用
loginAuthenticationProvider.setUserDetailsService(customUserDetailsService);
loginAuthenticationProvider.setPasswordEncoder(customPasswordEncoder);
auth.authenticationProvider(loginAuthenticationProvider);
super.configure(auth);

3.security國際化問題

在使用security過程中,因為原生代碼中DaoAuthenticationProvider這個類的additionalAuthenticationChecks方法

會在你前端頁面登陸失敗的時候返回Bad credentials這個消息。這會讓系統看上去不那么友好。

解決方法是:

1.在resources文件夾中添加messages_zh_CN.properties這個文件。自定義你想返回前端的話。

2.自定義登錄驗證類loginAuthenticationProvider(最后放出完整代碼)

3.在自定義security的配置類中做相應配置

完整代碼:

1.自定義security配置類

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    private final CustomPasswordEncoder customPasswordEncoder;
    private final CustomUserDetailsService customUserDetailsService;
    private final CommonProperties commonProperties;

    @Autowired
    public SecurityConfiguration(CustomPasswordEncoder customPasswordEncoder, CustomUserDetailsService customUserDetailsService, CommonProperties commonProperties) {
        this.customPasswordEncoder = customPasswordEncoder;
        this.customUserDetailsService = customUserDetailsService;
        this.commonProperties = commonProperties;
    }

    @Autowired
    private LoginAuthenticationProvider loginAuthenticationProvider;

    /**
     * http配置
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/doLogin")
                // 指定登錄頁面及成功后跳轉頁面
                .successForwardUrl("/loadIndex")
                .and().authorizeRequests()
                .antMatchers(commonProperties.getAllowList())
                // 上述url所有用戶都可訪問(無需登錄)
                .permitAll()
                .and()
                .authorizeRequests()
                .anyRequest()
                // 除上面之外的都需要登錄
                .authenticated()
                .and()
                .logout()
                // 指定登出url
                .logoutUrl("/doLogout")
                .invalidateHttpSession(true)
                .and()
                .headers()
                .frameOptions()
                // frame框架
                .sameOrigin()
                // csrf跨域保護忽略特殊url
                .and().csrf().ignoringAntMatchers(commonProperties.getAllowList());
    }

    /**
     * 靜態資源放行
     * @param web
     */
    @Override
    public void configure(WebSecurity web) {
        //靜態資源過濾
        web.ignoring().antMatchers("/**/*.js", "/**/*.css", "/**/*.png", "/**/*.jpg", "/**/*.gif", "/**/*.map");
    }

    /**
     * 自定義認證方法
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        loginAuthenticationProvider.setUserDetailsService(customUserDetailsService);
        loginAuthenticationProvider.setPasswordEncoder(customPasswordEncoder);
        auth.authenticationProvider(loginAuthenticationProvider);
        super.configure(auth);
    }

    /**
     * 自定義認證管理類
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 自定義密碼加密類
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

}

2.自定義賬號存儲類

@Service
public class CustomUserDetailsService implements UserDetailsService {
    private final AuthTokenService authTokenService;
    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    public CustomUserDetailsService(AuthTokenService authTokenService) {
        this.authTokenService = authTokenService;
    }

    /**
     * 將用戶詳細信息放入security session中
     * @param username
     * @return
     */
    @Override
    public UserDetails loadUserByUsername(String username) {
        try{
            Assert.isTrue(StringUtils.isNotBlank(username),"用戶或密碼錯誤,請重新輸入");
            // 通過用戶名獲取用戶詳細信息,構造權限
            UserDTO userInfo = authTokenService.getUserDetail(username);
            ResourceBO resource = userInfo.getResource();
            List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
            if(resource != null){
                buildAuth(resource, authorities);
            }
            return new UserBO(userInfo, authorities);
        }catch (IllegalArgumentException e){
            throw new UsernameNotFoundException("用戶或密碼錯誤,請重新輸入");
        }catch (Exception e){
            log.error("獲取用戶信息異常",e);
            throw new UsernameNotFoundException("用戶或密碼錯誤,請重新輸入");
        }
    }
}

3.自定義密碼加密類

@Service
public class CustomPasswordEncoder implements PasswordEncoder {
    @Override
    public String encode(CharSequence charSequence) {
        return charSequence.toString();
    }

    @Override
    public boolean matches(CharSequence charSequence, String s) {
        return s.equals(encode(charSequence));
    }
}

4.自定義security認證

@Component
public class LoginAuthenticationProvider extends DaoAuthenticationProvider {

    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    @Autowired
    private CustomPasswordEncoder customPasswordEncoder;

    @Autowired
    private void setJdbcUserDetailsService() {
        setUserDetailsService(customUserDetailsService);
    }

    /**
     * 自定義security國際化
     */
    @PostConstruct
    public void initProvider() {
        ReloadableResourceBundleMessageSource localMessageSource = new ReloadableResourceBundleMessageSource();
        localMessageSource.setBasenames("messages_zh_CN");
        messages = new MessageSourceAccessor(localMessageSource);
    }

    /**
     * 復寫認證失敗方法
     * @param userDetails
     * @param authentication
     * @throws AuthenticationException
     */
    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        if (authentication.getCredentials() == null) {
            logger.debug("Authentication failed: no credentials provided");
            throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        }
        String presentedPassword = authentication.getCredentials().toString();
        if (!customPasswordEncoder.matches(presentedPassword, userDetails.getPassword())) {
            logger.debug("Authentication failed: password does not match stored value");
            throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        }
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM