spring security gradle 多种登录方式实现

项目结构图

在这里插入图片描述

build.gradle

plugins {
	id 'org.springframework.boot' version '2.1.16.RELEASE'
	id 'io.spring.dependency-management' version '1.0.9.RELEASE'
	id 'java'
	id 'war'
}

group = 'com.sky'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenLocal()
	maven { url 'https://maven.aliyun.com/nexus/content/repositories/google' }
	maven { url 'https://maven.aliyun.com/nexus/content/groups/public' }
	maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter'}
	maven { url 'https://repo.spring.io/snapshot' }
	maven { url 'https://repo.spring.io/milestone' }
	maven { url 'https://plugins.gradle.org/m2/' }
	
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-devtools'
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.boot:spring-boot-starter-security'
	implementation 'org.springframework.security:spring-security-test'
	providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test{
	//useJUnitPlatform()
}

application.yml

server:
  port: 8081

SpringBootGradleApplication

@EnableGlobalMethodSecurity(prePostEnabled = true)
@SpringBootApplication
public class SpringBootGradleApplication extends SpringBootServletInitializer{
	public static void main(String[] args) {
		SpringApplication.run(SpringBootGradleApplication.class, args);
	}
	/** 使用外置的tomcat启动,需要继承SpringBootServletInitializer */
	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
		return application.sources(SpringBootGradleApplication.class);
	}
}

SysUser 用户信息,用于登录

mobile用于手机登录

public class SysUser {
	private int id;
	private String username;
	private String password;
	private String mobile;
	
	private List<String> roles;
	
	public SysUser() {}
	public SysUser(int id, String username, String password,String mobile) {
		this.id = id;
		this.username = username;
		this.password = password;
		this.mobile = mobile;
	}
	//...省略get set
	public SysUser addRole(String roleName) {
		if(this.roles == null) {
			this.roles = new ArrayList<>();
		}
		roles.add(roleName);
		return this;
	}
}

用户service类,构造用户数据,并实现UserDetailsService

public interface SysUserService {
	/** 根据账号获取用户 */
	SysUser getUserByAccount(String account);
	/** 根据电话获取用户 */
	SysUser getUserByMobile(String mobile);
}
@Service
public class SysUserServiceImpl implements SysUserService , UserDetailsService {
	private static Map<String, SysUser> USER_MAP = new HashMap<>(16);
	private static Map<String, String> USER_MODEL_MAP = new HashMap<>(16);
	static{
		//密码都是123456
		String encodePass = "{bcrypt}$2a$10$t8D4CmkVOCx834key9Mz5OL0M3MJlgd2c4hV3dxfl4zTXgJG1nJu.";
		USER_MAP.put("admin", new SysUser(1,"admin",encodePass,"19935588101").addRole("ROLE_EMPLOYEE").addRole("ROLE_ADMIN"));
		USER_MODEL_MAP.put("19935588101", "admin");
		USER_MAP.put("lisa", new SysUser(2,"lisa",encodePass,"19935588102").addRole("ROLE_EMPLOYEE"));
		USER_MODEL_MAP.put("19935588102", "lisa");
		USER_MAP.put("zhaosi", new SysUser(3,"zhaosi",encodePass,"19935588103"));
		USER_MODEL_MAP.put("19935588103", "zhaosi");
	}
	
	@Override
	public SysUser getUserByAccount(String account) {
		return USER_MAP.get(account);
	}
	@Override
	public SysUser getUserByMobile(String mobile) {
		String account = USER_MODEL_MAP.get(mobile);
		return getUserByAccount(account);
	}

	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		if (StringUtils.isEmpty(username)) {
			throw new UsernameNotFoundException("UserDetailsService没有接收到用户账号");
		}
		SysUser sysUser = getUserByAccount(username);
		if (sysUser == null) {
			throw new UsernameNotFoundException(String.format("用户'%s'不存在", username));
		}
		List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
		for (String role : sysUser.getRoles()) {
			// 封装用户信息和角色信息到SecurityContextHolder全局缓存中
			grantedAuthorities.add(new SimpleGrantedAuthority(role));
		}
		return new User(sysUser.getUsername(), sysUser.getPassword(), grantedAuthorities);
	}
}

SecurityConfig:spring security的配置类

@Configuration
@EnableWebSecurity
@Order(1) // 如果配置了多个WebSecurityConfigurerAdapter
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	@Bean
	PasswordEncoder passwordEncoder() {
		return PasswordEncoderFactories.createDelegatingPasswordEncoder();
	}
	
    @Autowired
    private AuthenticationManager authenticationManager;
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    
    @Autowired
    private UsernamePasswordAuthenticationProvider usernamePasswordAuthenticationProvider;
    @Autowired
    private MobileCodeAuthenticationProvider mobileCodeAuthenticationProvider;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .authenticationProvider(mobileCodeAuthenticationProvider)
            .authenticationProvider(usernamePasswordAuthenticationProvider);
    }
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		// 匹配的资源任何人都可以访问
		http.authorizeRequests().antMatchers("/", "/home").permitAll();
		// 访问任何资源都需要通过身份验证
		http.authorizeRequests().anyRequest().authenticated();
		// 指定登录页面,任何人都可以访问
		http.authorizeRequests().and().formLogin()
			.loginPage("/login")// 指定登录页面,  spring security有默认的登录页面
			.permitAll().and();
		// 指定登出页面,任何人都可以访问
		http.authorizeRequests().and().logout().permitAll().and();
		
		//增加过滤器
		http.addFilterBefore(mobileCodeAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
	}
	@Bean
	public MobileCodeAuthenticationProcessingFilter mobileCodeAuthenticationProcessingFilter() {
		MobileCodeAuthenticationProcessingFilter filter = new MobileCodeAuthenticationProcessingFilter();
		filter.setAuthenticationManager(authenticationManager);
		return filter;
	}
}

用户名密码认证

UsernamePasswordAuthenticationProvider

/** 用户名密码认证 */
@Component
public class UsernamePasswordAuthenticationProvider implements AuthenticationProvider {
	@Autowired UserDetailsService userDetailsService;
	@Autowired PasswordEncoder passwordEncoder;
	
	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
        String password = (String) authentication.getCredentials();
        if (!StringUtils.hasText(password)) {
            throw new BadCredentialsException("密码不能为空");
        }
        UserDetails user = userDetailsService.loadUserByUsername(username);
        if (null == user) {
            throw new BadCredentialsException("用户不存在");
        }
        //校验密码
        if (!passwordEncoder.matches(password, user.getPassword())) {
        	throw new BadCredentialsException("用户名或密码不正确");
        } 
        UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(username, password, user.getAuthorities());
        result.setDetails(authentication.getDetails());
        return result;
	}
	@Override
	public boolean supports(Class<?> authentication) {
		boolean flag = UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
		System.out.println(this.getClass().getName() + "---supports:" + flag);
        return flag;
	}
}

手机验证码认证

MobileCodeAuthenticationProcessingFilter
MobileCodeAuthenticationToken
MobileCodeAuthenticationProvider

public class MobileCodeAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
	public static final String SPRING_SECURITY_FORM_MOBILE_KEY = "mobile";
	public static final String SPRING_SECURITY_FORM_CODE_KEY = "code";
	private boolean postOnly = true;
	public MobileCodeAuthenticationProcessingFilter() {
		super(new AntPathRequestMatcher("/mobileCodeLogin", "POST"));
	}
	@Override
	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
			throws AuthenticationException, IOException, ServletException {
		if (postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
		}
		String mobile = getParamter(request, SPRING_SECURITY_FORM_MOBILE_KEY);
		String code = getParamter(request, SPRING_SECURITY_FORM_CODE_KEY);
		AbstractAuthenticationToken authRequest = new MobileCodeAuthenticationToken(mobile, code);
		setDetails(request, authRequest);
		return this.getAuthenticationManager().authenticate(authRequest);
	}
	protected String getParamter(HttpServletRequest request, String paramterName) {
		String value = request.getParameter(paramterName);
		if(value == null) {
			return "";
		}
		return value.trim();
	}
	protected void setDetails(HttpServletRequest request, AbstractAuthenticationToken authRequest) {
		authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
	}
}
public class MobileCodeAuthenticationToken extends AbstractAuthenticationToken {
	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
	private final Object principal;
	private String credentials;
	public MobileCodeAuthenticationToken(Object principal, String credentials) {
		super(null);
		this.principal = principal;
		this.credentials = credentials;
		setAuthenticated(false);
	}
	public MobileCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
		super(authorities);
		this.principal = principal;
		this.credentials = null;
		super.setAuthenticated(true); // must use super, as we override
	}
	public String getCredentials() {
		return this.credentials;
	}
	public Object getPrincipal() {
		return this.principal;
	}
	public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
		if (isAuthenticated) {
			throw new IllegalArgumentException(
					"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
		}
		super.setAuthenticated(false);
	}
	@Override
	public void eraseCredentials() {
		super.eraseCredentials();
		credentials = null;
	}
}
@Component
public class MobileCodeAuthenticationProvider implements AuthenticationProvider {
	@Autowired UserDetailsService userDetailsService;
	@Autowired SysUserService sysUserService;
	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
	       String mobile = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
	        String code = (String) authentication.getCredentials();
	        if (!StringUtils.hasText(code)) {
	            throw new BadCredentialsException("验证码不能为空");
	        }
	        SysUser sysUser = sysUserService.getUserByMobile(mobile);
	        if (null == sysUser) {
	            throw new BadCredentialsException("用户不存在");
	        }
	        UserDetails user = userDetailsService.loadUserByUsername(sysUser.getUsername());
	        if (null == user) {
	            throw new BadCredentialsException("用户不存在");
	        }
	        // 手机号验证码业务还没有开发,先用4个0验证
	        if (!code.equals("0000")) {
	            throw new BadCredentialsException("验证码不正确");
	        }
	        MobileCodeAuthenticationToken result = new MobileCodeAuthenticationToken(user.getUsername(), user.getAuthorities());
	        result.setDetails(authentication.getDetails());
	        return result;
	}
	@Override
	public boolean supports(Class<?> authentication) {
		boolean flag = MobileCodeAuthenticationToken.class.isAssignableFrom(authentication);
		System.out.println(this.getClass().getName() + "---supports:" + flag);
        return flag;
	}
}

MvcConfig

@Configuration
public class MvcConfig implements WebMvcConfigurer {
	public void addViewControllers(ViewControllerRegistry registry) {
		registry.addViewController("/home").setViewName("home");
		registry.addViewController("/").setViewName("home");
		registry.addViewController("/hello").setViewName("hello");
		registry.addViewController("/login").setViewName("login");
		registry.addViewController("/ignore").setViewName("ignore");
	}
}

相关html页面

login.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
      xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <title> login.html </title>
    </head>
    <body>
        <div th:if="${param.error}">
            Invalid username and password.
        </div>
        <div th:if="${param.logout}">
            You have been logged out.
        </div>
        <h1>账号登录</h1>
        <form th:action="@{ /login }" method="post">
            <div><label> User Name : <input type="text" name="username"/> </label></div>
            <div><label> Password: <input type="password" name="password"/> </label></div>
            <div><input type="submit" value="Sign In"/></div>
        </form>
        
        <h1>手机登录登录</h1>
        <form th:action="@{ /mobileCodeLogin }" method="post">
            <div><label> mobile number : <input type="text" name="mobile"/> </label></div>
            <div><label> code: <input type="text" name="code"/> </label></div>
            <div><input type="submit" value="Sign In"/></div>
        </form>
    </body>
</html>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <title>home.html</title>
    </head>
    <body>
        <h1>Welcome!</h1>
        <p>Click <a th:href="@{/hello}">here</a> to see a greeting.</p>
    </body>
</html>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
      xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <title>hello.html</title>
    </head>
    <body>
        <h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
        <form th:action="@{/logout}" method="post">
            <input type="submit" value="Sign Out"/>
        </form>
        <br/>
    </body>
</html>

测试

分别登录,成功!
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_26264237

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值