SpringBoot shiro多realm配置认证授权,直接运行版本

发布时间:2020-03-09
技术:springboot+shiro+thymeleaf+html+jquery+mevan

概述

场景介绍 Springboot shiro 用户名密码或手机号短信登录 (多realm配置认证、授权) ,用于web网站。选择用户名或者手机号登录后,系统认证授权。

详细

功能演示

一、目录结构

QQ图片20200305231132.png

二、功能讲解

(1)ShiroConfig 配置类

/**
 * shiro配置类
 */
@Configuration
public class ShiroConfig {

    @Bean("shiroFilterFactoryBean")
    ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/login");
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/index", "authc");
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/logout", "logout");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean
    public SecurityManager securityManager(UsernamePasswordRealm usernamePasswordRealm, MobileRealm mobileRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setAuthenticator(modularRealmAuthenticator());
        // 设置realms
        List<Realm> realms = new ArrayList<Realm>();
        realms.add(usernamePasswordRealm);
        realms.add(mobileRealm);
        securityManager.setRealms(realms);
        return securityManager;
    }

    //重要!!定义token与Realm关系,设置认证策略
    @Bean
    public ModularRealmAuthenticator modularRealmAuthenticator(){
        MyModularRealmAuthenticator authenticator = new MyModularRealmAuthenticator();
        FirstSuccessfulStrategy firstSuccessfulStrategy = new FirstSuccessfulStrategy();
        authenticator.setAuthenticationStrategy(firstSuccessfulStrategy);
        return authenticator;
    }


    @Bean
    MobileRealm mobileRealm() {
        MobileRealm mobileRealm = new MobileRealm();
        return mobileRealm;
    }

    @Bean
    @DependsOn({"hashedCredentialsMatcher"})
    UsernamePasswordRealm usernamePasswordRealm(HashedCredentialsMatcher matcher) {
        UsernamePasswordRealm usernamePasswordRealm = new UsernamePasswordRealm();
        usernamePasswordRealm.setCredentialsMatcher(matcher);
        return usernamePasswordRealm;
    }

    public static final String hashAlgorithName = "md5";
    public static final Integer hashIterations = 1024;
    @Bean(name = "hashedCredentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName(hashAlgorithName);//散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(hashIterations);//散列的次数,比如散列两次,相当于 md5(md5(""));
        return hashedCredentialsMatcher;
    }
  }

(2)MobileRealm 手机认证授权类

/**
 * 手机认证授权类
 * @author juno
 * @date   2020/03/03
 */
public class MobileRealm extends AuthorizingRealm {

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("授权逻辑-->MobileRealm.doGetAuthorizationInfo()");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 省略
        return  info;
    }

    /**
     * 主要是用来进行身份认证的,也就是说验证用户输入的手机号码是否正确。
     * @param authcToken
     * @return AuthenticationInfo
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
            throws AuthenticationException {
        System.out.println("Realm : " + this.getName());
        MobileToken mobileToken = null;
        if(authcToken instanceof MobileToken){
            mobileToken = (MobileToken) authcToken;
        }else{
            return null;
        }
        //例子1,模拟手机
        String mobile = mobileToken.getMobile();
        //模拟实体类
        Member member = new Member();
        member.setId(20L);
        member.setMobile(mobile);
        member.setLoginType(Member.LOGIN_TYPE_MOBILE);
        AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(member, mobile, this.getName());
        //清之前的授权信息
        super.clearCachedAuthorizationInfo(authcInfo.getPrincipals());
        SecurityUtils.getSubject().getSession().setAttribute("member", member);
        return authcInfo;

        // 例子2,走数据库判断
        //通过mobile从数据库中查找 Member对象
//        Member member = this.memberService.findMemberByMobile(mobile);
//        if(member == null){
//            throw new UnknownAccountException();
//        }
//        //手机-被禁止登录
//        if(StringUtils.equals(member.getStatus(),Member.STATUS_INACTIVE)){
//            throw new DisabledAccountException();
//        }
//        AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(member, mobile, this.getName());
//        //清之前的授权信息
//        super.clearCachedAuthorizationInfo(authcInfo.getPrincipals());
//        SecurityUtils.getSubject().getSession().setAttribute("member", member);
//        return authcInfo;
    }

    @Override
    public boolean supports(AuthenticationToken authenticationToken){
        return authenticationToken != null && (authenticationToken instanceof MobileToken );
    }
}

(3)UsernamePasswordRealm 用户名密码认证授权类

/**
 * 用户名密码认证授权类
 * @author juno
 * @date   2020/02/28
 */
public class UsernamePasswordRealm extends AuthorizingRealm {

    public static final String CREDENTIALS_SALT = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("授权逻辑-->UsernamePasswordRealm.doGetAuthorizationInfo()");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 省略
        return info;
    }

    /**
     * 主要是用来进行身份认证的,也就是说验证用户输入的用户名和密码是否正确。
     * @param authcToken
     * @return AuthenticationInfo
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
            throws AuthenticationException {
        System.out.println("Realm : " + this.getName());
        UsernamePasswordToken usernamePasswordToken = null;
        if(authcToken instanceof UsernamePasswordToken){
            usernamePasswordToken = (UsernamePasswordToken) authcToken;
        }else{
            return null;
        }
        String username = usernamePasswordToken.getUsername();
        String password = new String((char[]) usernamePasswordToken.getPassword());
        //例子1,模拟用户名
        if(!StringUtils.equals(username,"test")){
            //用户名不存在
            throw new UnknownAccountException();
        }
        String HASH_ALGORITH_NAME = "md5";
        Integer HASH_ITERATIONS = 1024;
        //加密后的密码
        String encryptionPassword =  new SimpleHash(HASH_ALGORITH_NAME, password, ByteSource.Util.bytes(CREDENTIALS_SALT) , HASH_ITERATIONS).toString();

        //模拟实体类
        Member member = new Member();
        member.setId(100L);
        member.setUsername(username);
        member.setLoginPassword(encryptionPassword);
        member.setLoginType(Member.LOGIN_TYPE_LOGINNAME);
        AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(member, member.getLoginPassword(), ByteSource.Util.bytes(CREDENTIALS_SALT), this.getName());
        //清之前的授权信息
        super.clearCachedAuthorizationInfo(authcInfo.getPrincipals());
        SecurityUtils.getSubject().getSession().setAttribute("member", member);
        return authcInfo;

        // 例子2,走数据库判断
        //通过username从数据库中查找 Member对象
//        Member member = this.memberDao.findMemberByUsername(username);
//        //用户名不存在
//        if(member == null){
//            throw new UnknownAccountException();
//        }else{
//        String HASH_ALGORITH_NAME = "md5";
//        Integer HASH_ITERATIONS = 1024;
//        String encryptionPassword =  new SimpleHash(HASH_ALGORITH_NAME, password, ByteSource.Util.bytes(CREDENTIALS_SALT) , HASH_ITERATIONS).toString();
//            //判断密码是否正确
//            if(!StringUtils.equals(member.getLoginPassword(),encryptionPassword)){
//                throw new IncorrectCredentialsException();
//            }
//
//        }
//        //用户名被禁止登录
//        if(StringUtils.equals(member.getStatus(),Member.STATUS_INACTIVE)){
//            throw new DisabledAccountException();
//        }
//        AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(member, member.getLoginPassword(), ByteSource.Util.bytes(credentialsSalt), this.getName());
//        //清之前的授权信息
//        super.clearCachedAuthorizationInfo(authcInfo.getPrincipals());
//        SecurityUtils.getSubject().getSession().setAttribute("member", member);
//        return authcInfo;
    }

    @Override
    public boolean supports(AuthenticationToken authenticationToken){
        return authenticationToken != null && (authenticationToken instanceof UsernamePasswordToken);
    }
}

(3)MemberController 会员控制类

/**
 * 会员控制类
 */
@Controller
public class MemberController {

    @Resource
    private MemberService memberService;

    /**
     * 登录页
     */
    @GetMapping(value = {"/", "/login"})
    public String login() {
        return "/login";
    }

    /**
     * 主页
     */
    @GetMapping(value = {"/index"})
    public String index() {
        return "/index";
    }

    /**
     * 用户名登录
     */
    @RequestMapping("/usernameLogin")
    @ResponseBody
    public Message authLogin(HttpServletRequest request) throws Exception {
        HashMap<String, Object> paramMap = CommonUtil.checkRequestParam(request, new String[]{"username", "password"});
        return this.memberService.usernameLogin(paramMap.get("username").toString(), paramMap.get("password").toString());
    }

    /**
     * 手机登录
     */
    @RequestMapping("/mobileLogin")
    @ResponseBody
    public Message mobileLogin(HttpServletRequest request) throws Exception {
        HashMap<String, Object> paramMap = CommonUtil.checkRequestParam(request, new String[]{"mobile", "smsValidateCode"});
        return this.memberService.mobileLogin(paramMap.get("mobile").toString(), paramMap.get("smsValidateCode").toString());
    }

}


三、前端页面

(1)login.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>登录页</title>
</head>
<link rel="stylesheet" href="/css/login.css">
<script src="/js/jquery-1.12.4.min.js"></script>
<script src="/js/ajax.js"></script>
<script src="/js/login.js"></script>
<body>

<h1>&nbsp;&nbsp;登录页</h1>
<!--header begin-->
<div class="header">
    <div class="container cl">
        <h1 class="site-logo">
            <div></div>
        </h1>

    </div>
</div>
<!--header end-->

<div class="login">
    <div class="login-bg"></div>
    <div class="container">
        <div class="login-box">
            <ul class="cl" id="tab">
                <li class="active"><a href="javascript: void(0);">手机登录</a></li>
                <li><a href="javascript: void(0);">用户名登录</a></li>
            </ul>

            <div class="panel" id="panel">
                <div style="display: block;">
                    <form action="" class="login-form quick-login">
                        <div class="item">
                            <input class="input-txt" id="mobile"  type="text" name="mobile" placeholder="手机号码" value="13318816600" />
                            <div class="error-message"></div>
                        </div>
                        <div class="item">
                            <div class="input">
                                <input class="input-txt" id="smsValidateCode" type="text" name="smsValidateCode" placeholder="动态密码" value="8461"/>
                                <div class="cover">
                                    <a style="cursor: pointer" href="javascript: void(0);">获取动态密码</a>
                                </div>
                            </div>
                            <div class="error-message"></div>
                        </div>
                        <div class="item">
                            <button id="mobileLoginButton" type="button" class="btn" onclick="mobileLogin()">登录</button>
                        </div>
                    </form>
                </div>
                <div>
                    <form action="" class="login-form quick-login">
                        <div class="item">
                            <input class="input-txt" type="text" name="username" id="username" placeholder="用户名" value="test"/>
                            <div class="error-message"></div>
                        </div>
                        <div class="item">
                            <input class="input-txt" type="password" name="password" id="password" placeholder="请输入密码" value="1111"/>
                            <div class="error-message"></div>
                        </div>
                        <div class="item">
                            <div class="error-message"></div>
                        </div>
                        <div class="item cl">
                            <div class="fl">
                                <input type="checkbox" title="" class="checkbox"/>记住密码
                            </div>
                            <div class="fr">
                                <a href="">忘记密码?</a>
                            </div>
                        </div>
                        <div class="item">
                            <button id="usernameLoginButton" type="button" class="btn" onclick="usernameLogin();">登录</button>
                        </div>
                    </form>
                </div>
            </div>

            <div class="toolbar cl">
                <div class="fr">
                    <a href="javascript: void(0);" class="register-btn">立即注册</a>
                </div>
            </div>
        </div>
    </div>
</div>

</body>
</html>

(2)index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>index 页</title>
</head>
<body>
<h1>index页</h1>
<div th:if="${session.member.loginType == '0'}">
    <h3>用户名:
        <span th:text="${session.member.username}"></span>
    </h3>
</div>
<span th:if="${session.member.loginType == '1'}">
    <h3>手机号码:
        <span th:if="${session.member.loginType == '1'}" th:text="${session.member.mobile}"></span>
    </h3>
</span>

<span>&nbsp;</span>
<span>&nbsp;</span>
<span>&nbsp;</span>
<div>
    <a style="font-size: 20px;color: red;" href="/logout">登出</a>
</div>

</body>
</html>

四、运行

http://127.0.0.1:8080   登录页

http://127.0.0.1:8080/index  首页(需要鉴权才能进入)

手机登录:

1.png







2.png

用户名登录

3.png

4.png


谢谢大家观看~


联系方式

QQ:359355126    蓝蓝的天 


本实例支付的费用只是购买源码的费用,如有疑问欢迎在文末留言交流,如需作者在线代码指导、定制等,在作者开启付费服务后,可以点击“购买服务”进行实时联系,请知悉,谢谢
手机上随时阅读、收藏该文章 ?请扫下方二维码