Spring Security实现多种方式认证

主要提供两种示例的认证方式,之后如果还有其它需要认证的方式可以效仿

1、认证构成

主要把认证代码分成4部分:

1、token携带需要认证的信息

2、provider提供认证服务

3、config配置需要提供的认证服务

4、业务代码中的认证

2、第一种认证方式DaoAuthenticationProvider

用户名密码认证,这是由spring security提供的最常用的认证方式,我们主要只需要关注三个类UserDetails用户信息实体类,UserDetailService自定义实现类获取用户信息,PasswordEncoder根据用户密码加载用户信息的加密方法。

1、config配置

import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.xyqq.app.user.impl.UserDetailsServiceImpl;

@EnableWebSecurity
@EnableWebMvc
public class WebMvcConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer {
    
    @Bean
    public PasswordEncoder passwordEncoder() {
       return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
       return super.authenticationManagerBean();
    }

    @Bean
    @Override
    public UserDetailsService userDetailsService() {
       return new UserDetailsServiceImpl();
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
       DaoAuthenticationProvider daoAuthProvider = new DaoAuthenticationProvider();
       daoAuthProvider.setUserDetailsService(this.userDetailsService());
       daoAuthProvider.setPasswordEncoder(this.passwordEncoder());
       auth.authenticationProvider(daoAuthProvider);
    }
}

继承WebSecurityConfigurerAdapter 类,重写config方法,摄者AuthenticationManagerBuilder,童工认证服务。重写DaoAuthenticationProvider 中的userDetailService方法,每个系统都有自己不同字段用户名去获取用户信息,所以这时候需要重写获取用户信息的方法。重写PsswordEncoder,系统对于用户密码的加密也是不同的,所以重写密码的加密解密方式。

2、业务层认证
private void login(String username, String password) throws BusinessException {
    try {
       LOGGER.info("Start to login {} ...", username);
       UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
       Authentication authentication = this.authenticationManager.authenticate(token);
       SecurityContextHolder.getContext().setAuthentication(authentication);
       LOGGER.info("User {} login successfully", this.getLoginUserFromSession());
    } catch (Exception ex) {
       LOGGER.error("User {} login failed", username, ex);
       throw new BusinessException(ErrorCodes.Login_Failed);
    }
}

 因为DaoAuthenticationProvider是由Spring Security提供的,所以我们不需要在写provider和token,当进行authenticationManager认证完之后会返回一个Authentication对象,我们使用SecurityContextHolder.getContext().serAuthentication()方法即可认证完毕。

3、第二种认证方式,微信小程序openId

1、config配置 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.xyqq.app.security.WeChatAuthenticationProvider;
import com.xyqq.app.user.UserService;

@EnableWebSecurity
@EnableWebMvc
public class WebMvcConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer {

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
       return super.authenticationManagerBean();
    }

    @Autowired
    private UserService userService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
       auth.authenticationProvider(new WeChatAuthenticationProvider(userService));
    }
}

 继承WebSecurityConfigurerAdaptor,重新实现configure方法,向AuthenticationMangerBuider中注册信息的认证服务。

2、创建认证token类
import java.util.ArrayList;
import java.util.Collection;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;

public class WeChatAuthenticationToken extends AbstractAuthenticationToken {

    private static final long serialVersionUID = -3625665688626567368L;

    private Collection<GrantedAuthority> authorities = new ArrayList<>();
    private Object principal;
    private Object credentials;
    private String openId;

    public WeChatAuthenticationToken(String openId) {
       super(null);
       this.openId = openId;
    }

    public void setPrincipal(Object principal) {
       this.principal = principal;
    }

    @Override
    public Object getPrincipal() {
       return this.principal;
    }

    @Override
    public Object getCredentials() {
       return this.credentials;
    }

    @Override
    public Collection<GrantedAuthority> getAuthorities() {
       return this.authorities;
    }

    public void setAuthorities(Collection<? extends GrantedAuthority> authorities) {
       this.authorities.addAll(authorities);
    }

    public String getOpenId() {
       return openId;
    }

}

 这个认证服务是根据用户微信小程序的openId来进行认证的,所以我们需要继承AbstractAuthenticationTaoken然后重新提供我们需要认证的变量openId。

3、创建认证服务provider

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.xyqq.app.user.UserInfo;
import com.xyqq.app.user.UserService;
import com.xyqq.app.user.domains.User;

public class WeChatAuthenticationProvider implements AuthenticationProvider {

    private static final Logger LOGGER = LoggerFactory.getLogger(WeChatAuthenticationProvider.class);

    private UserService userService;

    public WeChatAuthenticationProvider(UserService userService) {
       this.userService = userService;
    }

    @Override
    public boolean supports(Class<?> authentication) {
       return authentication.equals(WeChatAuthenticationToken.class);
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

       WeChatAuthenticationToken token = (WeChatAuthenticationToken) authentication;
       String openId = token.getOpenId();
       LOGGER.info("Start to authenticate {} ...", openId);

       UserDetails loadedUser = null;
       try {
          User user = this.userService.getByOpenId(openId);
          loadedUser = new UserInfo(user);
       } catch (UsernameNotFoundException notFound) {
          throw notFound;
       } catch (Exception repositoryProblem) {
          throw new InternalAuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
       }

       token.setPrincipal(loadedUser);
       token.setDetails(loadedUser);
       token.setAuthorities(loadedUser.getAuthorities());
       authentication.setAuthenticated(true);
       SecurityContextHolder.getContext().setAuthentication(authentication);
       return authentication;
    }
}

实现AuthenticationProvider接口,里面需要重写两个方法:supports(Class<?> authentication)和 authenticate(Authentication authentication),其中supports是指定这个provider所需要支持的认证token类,也就是我们上面创建的token类。第二个方法是进行用户的信息认证,使用系统的userService根据openId获取用户信息并进行认证。

4、业务层认证
WeChatAuthenticationToken token = new WeChatAuthenticationToken(wechatSession.getOpenid());
this.authenticationManager.authenticate(token);

4、附加 

在继承了WebSecurityConfigurerAdapter中,去重写configure(AuthenticationManagerBuilder auth)方法,去个AuthenticationManagerBuilder设置认证服务,里面可以设置多个provider,认证的时候会根据顺序执行下去,并找到对应的认证服务

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(new WeChatAuthenticationProvider(userService));
    auth.authenticationProvider(new UserAuthenticationProvider());
    auth.authenticationProvider(new TokenAccessAuthenticationProvider(tokenService));

    DaoAuthenticationProvider daoAuthProvider = new DaoAuthenticationProvider();
    daoAuthProvider.setUserDetailsService(this.userDetailsService());
    daoAuthProvider.setPasswordEncoder(this.passwordEncoder());
    auth.authenticationProvider(daoAuthProvider);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值