SpringBoot 整合shiro实现多Realm的控制

SpringBoot 整合shiro实现多Realm的控制

一、场景

假设在我们的系统中 有两个角色 用户 和 管理员
用户有100W 管理员只有 2个

1、假设是让你设计这个表 你会怎么设计?

经典的五张表
经典五张表
存在一个问题,用户和管理员 都是用户? 难道你要设计到一张表
现在的管理员登陆:通过用户名查询 需要查询100W+条数据 工作量太大

如果是我们能够将这两个角色的数据设计到两张表里面
管理员(2个人) 和100W人的用户表

2、管理员的表和用户的表 假设我们都使用shiro来进行认证 ? 怎么办?

设计思路
就是在用户登陆时候 设置一个 登陆类型
重写 UserNameAndPasswordToken 在执行正式的登陆的时候?根据登陆的类型选择 我们要使用的Realm 最终去进行认证 那么这个功能就搞定了

3、存在两个问题

​ 1:在哪里去做选择 执行哪一个realm?
​ ModularRealmAuthenticator.java类进行拓展(直接和realm打交道的)
​ 2:我们的登陆类型如何来定

二、编写代码

1、导包
 <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、配置application.properies
#Thymeleaf的配置
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.cache=false
3、编写登陆页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

   下面是User的登陆<br>
<form action="/userLogin" method="post">
    用户名:<input type="text" name="userName"><br>
    密码:<input type="password" name="password"><br>
    <input type="submit" value="用户登陆">
</form>

<hr>
   下面是Admin的登陆
   <form action="/adminLogin" method="post">
       用户名:<input type="text" name="userName"><br>
       密码:<input type="password" name="password"><br>
       <input type="submit" value="用户登陆">
   </form>

</body>
</html>
4、编写user模块登陆成功的页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

this is user index page  And you?

</body>
</html>
5、编写Admin模块登陆成功的页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

   this is admin index page And you?

</body>
</html>
6、编写登陆类型的枚举
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();
    }
}

7、编写校验器
public class CustomModularRealmAuthenticator extends ModularRealmAuthenticator {

    /**
     * 想干一件事
     *    就是通过传入数据的类型  来选择使用哪一个Realm
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
        //做Realm的一个校验
        assertRealmsConfigured();
        //获取前端传递过来的token
        CustomToken customToken=(CustomToken)authenticationToken;
        //现在就可以获取这个登陆的类型了
        String loginType = customToken.getLoginType();  //  登陆类型   1:User   Admin
        //获取所有的realms()
        Collection<Realm> realms = getRealms();
        //登陆类型对应的所有realm全部获取到
        Collection<Realm> typeRealms=new ArrayList<>();
        for (Realm realm:realms){
            //realm类型和现在登陆的类型做一个对比
            if(realm.getName().contains(loginType)){   //就能分开这两个realm
                typeRealms.add(realm);
            }
        }

        if(typeRealms.size()==1){
             return doSingleRealmAuthentication(typeRealms.iterator().next(),customToken);
        }else{
            return doMultiRealmAuthentication(typeRealms,customToken);
        }
    }
}

8、编写UserController
@Controller
public class UserController {

    //用户登陆的类型
    private static final String LOGIN_TYPE= LoginType.USER.toString();

    private Logger logger= LoggerFactory.getLogger(UserController.class);

    /**
     * 用户登陆的方法
     * @param user
     * @return
     */
    @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()){
                //说明认证是成功的
                return "user_index";
            }
        }catch (UnknownAccountException err){  //用户名不对
            logger.error("用户名不对");
        }catch (IncorrectCredentialsException err){//密码不对
            logger.error("密码不对");
        }catch (Exception err){    //其他错误 造成登陆失败
            logger.error("其他错误造成了登陆错误");
        }
        return "login";
    }


}

9、编写AdminController
@Controller
public class AdminController {

    //用户登陆的类型
    private static final String LOGIN_TYPE= LoginType.ADMIN.toString();

    private Logger logger= LoggerFactory.getLogger(UserController.class);

    /**
     * 用户登陆的方法
     * @param admin
     * @return
     */
    @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_index";
            }
        }catch (UnknownAccountException err){  //用户名不对
            logger.error("用户名不对");
        }catch (IncorrectCredentialsException err){//密码不对
            logger.error("密码不对");
        }catch (Exception err){    //其他错误 造成登陆失败
            logger.error("其他错误造成了登陆错误");
        }
        return "login";
    }

}

10、编写User对象
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    private static final long serialVersionUID = 1841757056845722315L;
    private int id;
    private String userName;
    private String password;
}

11、编写Admin对象
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Admin implements Serializable {
    private int id;
    private String userName;
    private String password;
}

12、编写配置文件
@SpringBootConfiguration
public class ShiroConfig {

    //拦截的过滤器的配置
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(
            @Qualifier("securityManager")DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置下如果认证没有成功的登陆地址
        shiroFilterFactoryBean.setLoginUrl("/toLogin");
        Map<String,String> maps=new HashMap<>();
        maps.put("/toLogin","anon");
        maps.put("/userLogin","anon");
        maps.put("/adminLogin","anon");
        maps.put("/**","authc");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(maps);

        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        return shiroFilterFactoryBean;
    }

    //配置下SecurityManager
    @Bean
    public DefaultWebSecurityManager securityManager(){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //设置校验器
        securityManager.setAuthenticator(authenticator());
        List<Realm> realms=new ArrayList<>();
        realms.add(userRealm());
        realms.add(adminRealm());
        //设置Realm
        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;
    }


    //下面就是认证器的配置
    @Bean
    public CustomModularRealmAuthenticator authenticator(){
        CustomModularRealmAuthenticator authenticator = new CustomModularRealmAuthenticator();
        return authenticator;
    }
}
13、编写用户的Realm
public class UserRealm extends AuthorizingRealm {

    private Logger logger= LoggerFactory.getLogger(AdminRealm.class);


    @Override
    public String getName() {
        return "UserRealm";
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        logger.info("UserRealm执行了....");

        //固定的
        //第一步:获取用户名
        String userName = (String) authenticationToken.getPrincipal();
        //第二步:查询数据库
        //查询中....
        //查询中....
        if(!(userName.equals("xiaobobo"))){
            return null;
        }

        //下面就是查询出来的对象
        User user = new User(1, "xiaobobo", "123");
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getUserName(), user.getPassword(), getName());
        return authenticationInfo;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }
}
14、编写Admin的Realm
public class AdminRealm extends AuthorizingRealm {


    private Logger logger= LoggerFactory.getLogger(AdminRealm.class);

    @Override
    public String getName() {
        return "AdminRealm";
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //固定的
        //第一步:获取用户名
        String userName = (String) authenticationToken.getPrincipal();
        //第二步:查询数据库
        //查询中....
        //查询中....
        if(!(userName.equals("xiaowangzi"))){
            return null;
        }

        logger.info("AdminRealm执行了....");

        //下面就是查询出来的对象
        Admin admin = new Admin(1, "xiaowangzi", "123");
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(admin.getUserName(), admin.getPassword(), getName());
        return authenticationInfo;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }
}
15、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;
    }
}

16、登陆页面跳转的Controller编写
@Controller
public class IndexController {
    @RequestMapping("toLogin")
    public String toLogin(){
        return "login";
    }
}

希望大家关注我一波,防止以后迷路,有需要的可以加我Q讨论互相学习java ,学习路线探讨,经验分享与java Q:2415773436

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值