内容简介:不用spring-security自带的,自己实现一个provider,只做用户名密码校验,代码如下SecurityConfig配置如下实现UserDetailService接口,封装一个UserDetails对象,包含用户名密码,以及用户的权限
自定用户名密码验证
不用spring-security自带的,自己实现一个provider,只做用户名密码校验,代码如下
public class MyAuthenticationProvider extends DaoAuthenticationProvider { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication; String username = token.getName(); UserDetails userDetails = this.getUserDetailsService().loadUserByUsername(username); // 验证密码是否正确 if (!new BCryptPasswordEncoder().matches((CharSequence) token.getCredentials(), userDetails.getPassword())) { throw new AuthenticationServiceException("用户名或密码错误"); } return new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities()); } @Override public boolean supports(Class<?> authentication) { return UsernamePasswordAuthenticationToken.class.equals(authentication); } }
SecurityConfig配置如下
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private MyUserDetailService myUserDetailService; @Autowired private MyFilterSecurityInterceptor myFilterSecurityInterceptor; @Autowired private MyCustomAuthenticationFilter myCustomAuthenticationFilter; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/admin/**") .authenticated(); http.formLogin() .loginPage("/adminlogin") .loginProcessingUrl("/adminlogin") .failureUrl("/adminlogin?error") .defaultSuccessUrl("/admin/dashboard") .permitAll(); http.logout() .logoutRequestMatcher(new AntPathRequestMatcher("/admin/logout")) .logoutSuccessUrl("/adminlogin") .deleteCookies("JSESSIONID", "remember-me"); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) { auth.authenticationProvider(authenticationProvider()); } @Bean public AuthenticationProvider authenticationProvider() { MyAuthenticationProvider provider = new MyAuthenticationProvider(); provider.setPasswordEncoder(new BCryptPasswordEncoder()); provider.setUserDetailsService(myUserDetailService); return provider; } }
实现UserDetailService接口,封装一个UserDetails对象,包含用户名密码,以及用户的权限
@Service public class MyUserDetailService implements UserDetailsService { @Autowired private AdminUserService adminUserService; @Autowired private PermissionService permissionService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { AdminUser adminUser = adminUserService.findByUsername(username); List<Permission> permissions = permissionService.findByAdminUserId(adminUser.getId()); List<GrantedAuthority> grantedAuthorities = new ArrayList<>(); for (Permission permission : permissions) { GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permission.getName()); grantedAuthorities.add(grantedAuthority); } return new User(adminUser.getUsername(), adminUser.getPassword(), grantedAuthorities); } }
添加验证码校验
校验验证码通过实现 UsernamePasswordAuthenticationFilter
过滤器来处理,原码如下
@Component public class MyCustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter { @Autowired private SiteConfig siteConfig; @Autowired private AdminUserService adminUserService; @PostConstruct public void init() { SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler(); successHandler.setDefaultTargetUrl("/admin/dashboard"); setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/adminlogin", "POST")); setAuthenticationSuccessHandler(successHandler); setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler("/adminlogin?error")); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { // 只接受POST方式传递的数据 if (!"POST".equals(request.getMethod())) throw new AuthenticationServiceException("不支持非POST方式的请求!"); // 验证码验证 String requestCaptcha = request.getParameter("code"); String genCaptcha = (String) request.getSession().getAttribute("index_code"); if (StringUtils.isEmpty(requestCaptcha)) throw new AuthenticationServiceException("验证码不能为空!"); if (!genCaptcha.toLowerCase().equals(requestCaptcha.toLowerCase())) { throw new AuthenticationServiceException("验证码错误!"); } // 判断登陆次数及上限时间 String username = obtainUsername(request); AdminUser adminUser = adminUserService.findByUsername(username); if (adminUser == null) { throw new AuthenticationServiceException("用户名或密码错误!"); } else { if (adminUser.getAttempts() >= siteConfig.getAttempts()) { Calendar dateOne = Calendar.getInstance(), dateTwo = Calendar.getInstance(); dateOne.setTime(new Date()); dateTwo.setTime(adminUser.getAttemptsTime()); long timeOne = dateOne.getTimeInMillis(); long timeTwo = dateTwo.getTimeInMillis(); long minute = (timeOne - timeTwo) / (1000 * 60);// 转化minute if (minute < siteConfig.getAttemptsWaitTime()) { throw new AuthenticationServiceException( "密码错误超过" + siteConfig.getAttempts() + "次,账号已被锁定" + siteConfig.getAttemptsWaitTime() + "分钟"); } else { adminUser.setAttempts(0); } } } return super.attemptAuthentication(request, response); } @Override @Autowired public void setAuthenticationManager(AuthenticationManager authenticationManager) { super.setAuthenticationManager(authenticationManager); } }
SecurityConfig配置添加一个filter
@Override protected void configure(HttpSecurity http) throws Exception { // ... http.addFilterBefore(myCustomAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); // ... }
这里把验证码校验跟登录次数处理放在一个Filter里处理了,也可以分开,多加一个类来实现 UsernamePasswordAuthenticationFilter
即可
登录成功记住我
先开发一个实体类记录rememberMeToken
@Entity @Table public class RememberMeToken implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; private String username; private String series; private String tokenValue; private Date date; // getter , setter }
添加对应的repository
public interface RememberMeTokenRepository extends JpaRepository<RememberMeToken, Integer> { RememberMeToken getBySeries(String series); void deleteByUsername(String username); List<RememberMeToken> getAllByUsernameOrderByDate(String username); }
添加对应的service
@Service @Transactional public class PersistentTokenService implements PersistentTokenRepository { @Autowired private RememberMeTokenRepository rememberMeTokenRepository; @Autowired private SiteConfig siteConfig; @Override public void createNewToken(PersistentRememberMeToken token) { List<RememberMeToken> tokens = rememberMeTokenRepository.getAllByUsernameOrderByDate(token.getUsername()); if (tokens != null && tokens.size() >= siteConfig.getLoginPoints()) { int end = tokens.size() - siteConfig.getLoginPoints() + 1; for (int i = 0; i < end; i++) { rememberMeTokenRepository.delete(tokens.get(i)); } } RememberMeToken rememberMeToken = new RememberMeToken(); BeanUtils.copyProperties(token, rememberMeToken); rememberMeTokenRepository.save(rememberMeToken); } @Override public void updateToken(String series, String tokenValue, Date lastUsed) { RememberMeToken rememberMeToken = rememberMeTokenRepository.getBySeries(series); if (rememberMeToken != null) { rememberMeToken.setTokenValue(tokenValue); rememberMeToken.setDate(lastUsed); } } @Override public PersistentRememberMeToken getTokenForSeries(String seriesId) { RememberMeToken rememberMeToken = rememberMeTokenRepository.getBySeries(seriesId); if (rememberMeToken != null) { return new PersistentRememberMeToken(rememberMeToken.getUsername(), rememberMeToken.getSeries(), rememberMeToken.getTokenValue(), rememberMeToken.getDate()); } return null; } @Override public void removeUserTokens(String username) { rememberMeTokenRepository.deleteByUsername(username); } }
在 SecurityConfig 里配置一个记住我的服务Bean
@Autowired private PersistentTokenService persistentTokenService; @Bean public PersistentTokenBasedRememberMeServices persistentTokenBasedRememberMeServices() { PersistentTokenBasedRememberMeServices services = new PersistentTokenBasedRememberMeServices("remember-me" , myUserDetailService, persistentTokenService); services.setAlwaysRemember(true); return services; }
在校验验证码的过滤器中添加上记住我功能的服务
@Autowired private PersistentTokenBasedRememberMeServices persistentTokenBasedRememberMeServices; @PostConstruct public void init() { // ... setRememberMeServices(persistentTokenBasedRememberMeServices); }
至此,标题中列出的功能都实现了,具体代码见项目: https://github.com/tomoya92/spring-boot-security-demo
参考
原文链接:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Ajax邮箱、用户名唯一性验证实例代码
- 正则表达式验证用户名、密码、手机号码、身份证(推荐)
- 如果修改 git 已提交的用户邮箱和用户名
- 用户名(昵称)XSS浅析
- Git 免用户名密码访问代码库
- Sqlmap初体验,渗透拿到网站用户名密码
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。