Shiro的学习(四)

由于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的一个思路,更多的功能还在完善中。

发布了11 篇原创文章 · 获赞 6 · 访问量 191

猜你喜欢

转载自blog.csdn.net/X_Q_B_J/article/details/104563723
今日推荐