Spring Security 自定义用户认证

Security能帮助我们快速实现认证功能,下面介绍几种常用的方式

基础类配置

	@Configuration
	@EnableWebSecurity 
	public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
	}

不管哪种用户存储方式,都可以在这个方法配置实现

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

    }

内存中实现

适用于用户数量较少,开发时间较短的项目

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("feng")
                .password("fengPassword")
                .authorities("ROLE_USER")
        .and().withUser("name")
                .password("namePassword")
                .authorities("ROLE_MANAGER");
    }

基于JDBC

    @Autowired
    private DataSource dataSource;
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  		auth.jdbcAuthentication().dataSource(dataSource)
	         .usersByUsernameQuery("select username, password, from users where username = ?")
             .authoritiesByUsernameQuery("select username, authority from userauthorities where username = ?");
    }

一般数据库中密码不会明文存储,所以需要加上密码转码器,这里使用的是bcrypt强哈希加密

    @Autowired
    private DataSource dataSource;
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  		auth.jdbcAuthentication().dataSource(dataSource)
	         .usersByUsernameQuery("select username, password, from users where username = ?")
             .authoritiesByUsernameQuery("select username, authority from userauthorities where username = ?")
             .passwordEncoder(new BCryptPasswordEncoder());
    }

以LDAP作为存储

    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.ldapAuthentication()
                .userSearchBase("ou=perole")            // 从名为people的组织单元搜索用户
                .userSearchFilter("uid={0}")            
                .groupSearchBase("uid=groups")          // 从名为groups的组织单元搜索组
                .groupSearchFilter("member={0}")
                .passwordCompare()                      //配置密码比对
                .passwordAttribute("passcode")          //声明密码属性
        ;
    }

引用远程服务器

        auth.ldapAuthentication()
                .userSearchBase("ou=perole")            // 从名为people的组织单元搜索用户
                .userSearchFilter("uid={0}")
                .groupSearchBase("uid=groups")          // 从名为groups的组织单元搜索组
                .groupSearchFilter("member={0}")
                .passwordCompare()                      //配置密码比对
                .passwordAttribute("passcode")          //声明密码属性
                .and()
                    .contextSource().url("ldap://xxx")    
        ;
    }

推荐方式

实际开发中,我们很可能是用微服务,把认证服务(auth)和用户服务(user)分开。
user服务存储了所有的用户信息,以及用户(加盐加密后)的密码
auth服务需要校验账号密码时,调用user服务的校验接口进行校验

WebSecurityConfigurerAdapter

 	@Autowired
    private UserAuthProvider userAuthProvider;
 
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.authenticationProvider(userAuthProvider);
    }

实现 AuthenticationProvider 接口

public class UserAuthProvider implements AuthenticationProvider {
    private static Logger logger = LoggerFactory.getLogger(UserAuthProvider.class);

    protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();

    private UserDetailsService userDetailsService;

    private UserDetailsServiceImpl iUserDetailsService;

    public UserAuthProvider(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Autowired
    private IUserService iUserService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String userName = authentication.getName();
        String password = (String) authentication.getCredentials();

        // 调用微服务user的登录接口
        LoginDTO loginDTO = new LoginDTO();
        loginDTO.setLoginName(userName);
        loginDTO.setPassword(password);
        Object jsonObject = JSONObject.toJSON(iUserService.login(loginDTO).getData());
        
        User user = (User) JSONObject.toJavaObject((JSON) jsonObject, User.class);
        iUserDetailsService = new UserDetailsServiceImpl();
        UserDetails userDetails = null;
        if (user != null) {
            userDetails = iUserDetailsService.loadUserByUserDTO(user, loginDTO.getPassword());
        }
        if (userDetails == null) {
            logger.debug("Authentication failed: user doesn't exists!");
            throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        } else {
            if (userDetails != null) {
                return new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
            } else {
                logger.debug("Authentication failed: password does not match stored value!");
                throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
            }
        }
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

这里的报错有个坑,修改message里面的内容没有起效果,原因是他每次都new一个新的,所以我们输入的字符串不生效。

自定义抛错

不使用BadCredentialsException,比如账号密码锁定,可以用LockedException,我们也可以自定义一个错误类

public class AuthUsernameNotFoundException extends AuthenticationException {


    public AuthUsernameNotFoundException(String msg) {
        super(msg);
    }

    public AuthUsernameNotFoundException(String msg, Throwable t) {
        super(msg, t);
    }
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值