SpringBoot整合Shiro多验证登录功能的实现
SpringBoot是一个快速开发Spring应用的框架,而Shiro可以方便的实现安全认证和授权,两者结合,可以非常方便的实现多验证登录功能。
SpringBoot集成Shiro
首先需要添加Shiro和SpringBoot的依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version>
</dependency>
然后需要配置Shiro的SpringBoot启动类,添加@Configuration注解,并添加@Bean注解的ShiroFilterFactoryBean和DefaultWebSecurityManager。
@Configuration
public class ShiroConfiguration {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
...
return shiroFilterFactoryBean;
}
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm());
return securityManager;
}
@Bean
public MyRealm myRealm() {
return new MyRealm();
}
}
自定义Realm
接着需要自定义Realm,实现doGetAuthenticationInfo和doGetAuthorizationInfo方法。
public class MyRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//授权方法
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//认证方法
return null;
}
}
账号密码登录验证
对于账号密码登录验证,可以通过用户名和密码进行登录认证,实现doGetAuthenticationInfo方法。
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
String password = new String(token.getPassword());
//根据用户名和密码查询用户信息,可以通过MyBatis进行数据库操作
User user = userService.findByUsernameAndPassword(username, password);
if (user == null) {
throw new UnknownAccountException("用户名或密码错误");
}
return new SimpleAuthenticationInfo(username, password, getName());
}
关于User实体和UserService可以通过自己的业务逻辑进行实现。
手机验证码登录验证
对于手机验证码登录验证,则需要在认证方法中自定义Token,用来存储手机号和验证码的信息。
public class SmsToken implements AuthenticationToken {
private String phone;
private String code;
public SmsToken(String phone, String code) {
this.phone = phone;
this.code = code;
}
@Override
public Object getPrincipal() {
return phone;
}
@Override
public Object getCredentials() {
return code;
}
}
同时需要实现SmsRealm,在认证方法中进行手机号和验证码的比对。
public class SmsRealm extends AuthenticatingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
SmsToken token = (SmsToken) authenticationToken;
String phone = token.getPrincipal().toString();
String code = token.getCredentials().toString();
//查询手机号和验证码是否匹配
if (!"123456".equals(code)) {
throw new AuthenticationException("验证码错误");
}
return new SimpleAuthenticationInfo(phone, code, getName());
}
}
完整代码可以参考以下示例:
@Configuration
public class ShiroConfiguration {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/doLogin", "anon");
filterChainDefinitionMap.put("/login/sms", "anon");
filterChainDefinitionMap.put("/doSmsLogin", "anon");
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm());
return securityManager;
}
@Bean
public MyRealm myRealm() {
return new MyRealm();
}
@Bean
public SmsRealm smsRealm() {
return new SmsRealm();
}
}
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//授权方法
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
String password = new String(token.getPassword());
//根据用户名和密码查询用户信息,可以通过MyBatis进行数据库操作
User user = userService.findByUsernameAndPassword(username, password);
if (user == null) {
throw new UnknownAccountException("用户名或密码错误");
}
return new SimpleAuthenticationInfo(username, password, getName());
}
}
public class SmsRealm extends AuthenticatingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
SmsToken token = (SmsToken) authenticationToken;
String phone = token.getPrincipal().toString();
String code = token.getCredentials().toString();
//查询手机号和验证码是否匹配
if (!"123456".equals(code)) {
throw new AuthenticationException("验证码错误");
}
return new SimpleAuthenticationInfo(phone, code, getName());
}
}
@Controller
public class LoginController {
@GetMapping("/login")
public String login() {
return "login";
}
@GetMapping("/logout")
public String logout() {
return "logout";
}
@PostMapping("/doLogin")
public String doLogin(String username, String password) {
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
return "redirect:/index";
} catch (AuthenticationException e) {
return "error";
}
}
@GetMapping("/login/sms")
public String smsLogin() {
return "login_sms";
}
@PostMapping("/doSmsLogin")
public String doSmsLogin(String phone, String code) {
SmsToken token = new SmsToken(phone, code);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
return "redirect:/index";
} catch (AuthenticationException e) {
return "error";
}
}
}
其中包括了一个基本的登录页面、验证码登录页面,以及对应的Controller。在前端页面的form中添加对应的input即可实现不同方式的登录。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:springboot整合shiro多验证登录功能的实现(账号密码登录和使用手机验证码登录) - Python技术站