由于shiro本身默认的是单realm控制,如果业务中用户分布在不同的数据库,单realm就很难实现登录和权限管理的功能了
SpringBoot整合shiro实现多realm的简单实现过程:
在登录的时候设置一个登录类型(loginType),重UserNameAndPasswordToken,在执行subject.login的时候根据登录的类型选择我们要使用的realm去认证
1、项目pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
2、模板引擎配置
spring.thymeleaf.cache=false
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML5
3、编写前端页面
3.1、登录页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<base th:href="${#request.getContextPath()+'/'}">
</head>
<body>
<form action="/userLogin" method="post">
用户名: <input type="text" name="username"><br>
密码: <input type="password" name="password"><br>
<input type="submit" value="用户登录">
</form>
<hr>
<form action="/adminLogin" method="post">
用户名: <input type="text" name="username"><br>
密码: <input type="password" name="password"><br>
<input type="submit" value="管理员登录">
</form>
</body>
</html>
3.2、user.html页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<base th:href="${#request.getContextPath()+'/'}">
</head>
<body>
this is user index page!
</body>
</html>
3.3、amdin.html页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<base th:href="${#request.getContextPath()+'/'}">
</head>
<body>
this is admin page!
</body>
</html>
4、实体类
记住此处要实现序列化接口
4.1、user实体
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private static final long serialVersionUID = 1841757056845722315L;
private int id;
private String userName;
private String password;
}
4.2、admin实体
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Admin implements Serializable {
private int id;
private String userName;
private String password;
}
4、定义枚举类型
public enum LoginType {
USER("User"),ADMIN("Admin");
//登录类型
private String type;
private LoginType(String type){
this.type = type;
}
@Override
public String toString() {
return this.type.toString();
}
}
5、重写token
public class CustomToken extends UsernamePasswordToken {
//定义登录类型,选择执行哪个realm
private String loginType;
public CustomToken(String username,String password,String loginType){
super(username,password);
this.loginType=loginType;
}
public void setLoginType(String loginType) {
this.loginType = loginType;
}
public String getLoginType() {
return loginType;
}
}
6、编写controller
6.1、UserController
@Controller
public class UserController {
//用户登录的类型
private static final String LOGIN_TYPE = LoginType.USER.toString();
private Logger logger = LoggerFactory.getLogger(UserController.class);
@RequestMapping("userLogin")
public String login(User user){
//封装请求对象
CustomToken customToken = new CustomToken(user.getUsername(), user.getPassword(), LOGIN_TYPE);
//获取登录主体
Subject subject = SecurityUtils.getSubject();
try {
subject.login(customToken);
if(subject.isAuthenticated()){
//认证成功跳转到user页面
return "user";
}
} catch (UnknownAccountException e){
logger.error("用户名不对");
} catch (IncorrectCredentialsException e){
logger.error("密码不对");
} catch (Exception e){
logger.error("其他错误");
}
//返回登录页面
return "login";
}
}
6.2、AdminController
@Controller
public class AdminController {
private static final String LOGIN_TYPE = LoginType.ADMIN.toString();
private Logger logger = LoggerFactory.getLogger(AdminController.class);
@RequestMapping("adminLogin")
public String login(Admin admin){
CustomToken customToken = new CustomToken(admin.getUsername(), admin.getPassword(), LOGIN_TYPE);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(customToken);
if(subject.isAuthenticated()){
return "admin";
}
} catch (UnknownAccountException e){
logger.error("用户名不对");
} catch (IncorrectCredentialsException e){
logger.error("密码不对");
} catch (Exception e){
logger.error("就是不对");
}
return "login";
}
7、两个realm
7.1、UserRealm
public class UserRealm extends AuthorizingRealm {
@Override
public String getName() {
return "UserRealm";
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取用户名
String username = (String) authenticationToken.getPrincipal();
//查询数据库
if(!(username.equals("xbb"))){
return null;
}
//这里模拟数据库查询的结果
User user = new User(1,"xbb","123");
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
return simpleAuthenticationInfo;
}
}
7.2、AdminRealm
public class AdminRealm extends AuthorizingRealm {
@Override
public String getName() {
return "AdminRealm";
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String username = (String) authenticationToken.getPrincipal();
if(!(username.equals("xwz"))){
return null;
}
Admin admin = new Admin(1,"xwz","234");
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(admin.getUsername(), admin.getPassword(), getName());
return simpleAuthenticationInfo;
}
}
8.编写校验器
public class CustomModularRealmAuthenticator extends ModularRealmAuthenticator {
/**
* 通过传入数据的类型,来选择哪个realm
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
//realm校验
this.assertRealmsConfigured();
//获取前端传来的token
CustomToken customToken = (CustomToken)authenticationToken;
//获取登录类型
String loginType = customToken.getLoginType();
//获取所有的realms
Collection<Realm> realms = getRealms();
//定义一个集合存放获取到登录类型对应的realm
Collection<Realm> typeRealms = new ArrayList<>();
//?
for(Realm realm:realms){
System.out.println(realm.getName());
//将realm类型和现在登录的类型做一个对比
if(realm.getName().contains(loginType)){
typeRealms.add(realm);
}
}
if(typeRealms.size()==1){
return doSingleRealmAuthentication(typeRealms.iterator().next(),customToken);
}else {
return doMultiRealmAuthentication(typeRealms,customToken);
}
}
}
9、shiro.config配置
@SpringBootConfiguration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(
@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setLoginUrl("/toLogin");
Map<String,String> map = new HashMap<>();
map.put("/toLogin","anon");
map.put("/userLogin","anon");
map.put("/adminLogin","anon");
map.put("/**","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
@Bean
public DefaultWebSecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//设置校验器
securityManager.setAuthenticator(authenticator());
List<Realm> realms = new ArrayList<>();
realms.add(userRealm());
realms.add(adminRealm());
//设置realms
securityManager.setRealms(realms);
return securityManager;
}
@Bean
public UserRealm userRealm(){
UserRealm userRealm = new UserRealm();
return userRealm;
}
@Bean
public AdminRealm adminRealm(){
AdminRealm adminRealm = new AdminRealm();
return adminRealm;
}
/**
* 认证器的配置
* @return
*/
@Bean
public CustomModularRealmAuthenticator authenticator(){
CustomModularRealmAuthenticator authenticator = new CustomModularRealmAuthenticator();
return authenticator;
}
}
以上就是该项目的简单搭建,只是简述了一下SpringBoot整合shiro实现多realm的一个思路,更多的功能还在完善中。