SpringBoot&Shiro实现用户认证
SpringBoot&Shiro实现用户认证
实现思路
思路:实现认证功能主要可以归纳为3点
1.定义一个ShiroConfig配置类,配置 SecurityManager Bean , SecurityManager为Shiro的安全管理器,管理着所有Subject;
注:如果有不太清楚shiro的朋友,可以去各大学习平台上学习一下。
2.在ShiroConfig中配置 ShiroFilterFactoryBean ,它是Shiro过滤器工厂类,依赖SecurityManager ;
3.自定义Realm实现类,包含
doGetAuthorizationInfo()和doGetAuthenticationInfo()方法 ,
1.导入依赖
我们搭建好Spring Boot web程序后,导入Shiro,Mybatis,mysql, thymeleaf 相关依赖:
   <!-- SpringBoot Web容器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- SpringBoot集成mybatis框架 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>
        <!-- SpringBoot 测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- thymeleaf模版 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!-- mysql驱动7.0-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.40</version>
        </dependency>
        <!--druid 数据源监控-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!-- shiro权限 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
2.定义ShiroConfig配置类:
/**
 * @ClassName ShiroConfig
 * @Description TODO
 * @Author fqCoder
 * @Date 2020/2/29 3:08
 * @Version 1.0
 */
@Configuration
public class ShiroConfig {
        /**
         * 这是shiro的大管家,相当于mybatis里的SqlSessionFactoryBean
         * @param securityManager
         * @return
         */
        @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean(org.apache.shiro.mgt.SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            //登录
            shiroFilterFactoryBean.setLoginUrl("/login");
            //首页
            shiroFilterFactoryBean.setSuccessUrl("/index");
            //错误页面,认证不通过跳转
            shiroFilterFactoryBean.setUnauthorizedUrl("/403");
            //页面权限控制
            shiroFilterFactoryBean.setFilterChainDefinitionMap(ShiroFilterMapFactory.shiroFilterMap());
            //设置securityManager
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            return shiroFilterFactoryBean;
        }
        /**
         * web应用管理配置
         * @param shiroRealm
         * @return
         */
        @Bean
        public DefaultWebSecurityManager securityManager(Realm shiroRealm) {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(shiroRealm);
            return securityManager;
        }
        /**
         * 配置realm,用于认证和授权
         * @return
         */
        @Bean
        public MyShiroRealm shiroRealm() {
            MyShiroRealm shiroRealm = new MyShiroRealm();
            return shiroRealm;
        }
    }
3.创建ShiroFilterMapFactory类
注意:
1.这里要用LinkedHashMap 保证有序
2.filterChain基于短路机制,即最先匹配原则,
3.像anon、authc等都是Shiro为我们实现的过滤器,我给出了一张表,在文章尾附录,自行查看
/**
 * @ClassName ShiroFilterMapFactory
 * @Description TODO
 * @Author fqCoder
 * @Date 2020/2/29 3:09
 * @Version 1.0
 */
public class ShiroFilterMapFactory {
    public static Map<String, String> shiroFilterMap() {
//      设置路径映射,注意这里要用LinkedHashMap 保证有序
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        //对所有用户认证
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/logout", "logout");
        //对所有页面进行认证
        filterChainDefinitionMap.put("/**", "authc");
        return filterChainDefinitionMap;
    }
}
配置完了ShiroConfig后,实现自己的Realm,然后注入到SecurityManager里
4.实现Realm类
自定义Realm类需要继承 AuthorizingRealm 类,实现 doGetAuthorizationInfo()和doGetAuthenticationInfo()方法即可 ,
doGetAuthorizationInfo() 方法是进行授权的方法,获取角色的权限信息
doGetAuthenticationInfo()方法是进行用户认证的方法,验证用户名和密码
/**
 * @ClassName MyShiroRealm
 * @Description TODO
 * @Author fqCoder
 * @Date 2020/2/29 3:08
 * @Version 1.0
 */
@Service
public class MyShiroRealm  extends AuthorizingRealm {
    @Autowired
    private UserMapper userMapper;
    /**
     * 获取用户角色和权限
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }
    /**
     * 登录认证
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //获取用户输入的用户名密码
        String username= (String) token.getPrincipal();
        String password=new String((char[])token.getCredentials());
        System.out.println("用户输入--->username:"+username+"-->password:"+password);
        //在数据库中查询
        User userInfo=userMapper.selectByName(username);
        if (userInfo == null) {
            throw new UnknownAccountException("用户名或密码错误!");
        }
        if (!password.equals(userInfo.getPassword())) {
            throw new IncorrectCredentialsException("用户名或密码错误!");
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                userInfo, // 用户名
                userInfo.getPassword(), // 密码
                getName() // realm name
        );
        return authenticationInfo;
    }
}
doGetAuthorizationInfo()方法我们留到下一章实现,其中UnknownAccountException等异常为Shiro自带异常,Shiro具有丰富的运行时AuthenticationException层次结构,可以准确指出尝试失败的原因。
接下来我们实现dao层!
4.数据层
数据表设计的比较简单,方便操作
CREATE TABLE `tb_user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Compact;
然后创建一张对应的用户表User.class
public class User implements Serializable {
    private Integer id;
    private String username;
    private String password;
    ....get和set方法省略
}
定义接口UserMapper
这里用的是注解
/**
 * @ClassName UserMapper
 * @Description TODO
 * @Author fqCoder
 * @Date 2020/2/29 3:30
 * @Version 1.0
 */
public interface UserMapper {
    @Select("select * from tb_user where username=#{username}")
    User selectByName(String username);
}
注意:记得在Mapper接口上面加一个扫描注解@Mapper或者在boot启动类上加一个@MapperScan(value = "mapper包路径")注解
5.控制层
我们创建一个LoginController.class类
/**
 * @ClassName LoginController
 * @Description TODO
 * @Author fqCoder
 * @Date 2020/2/29 6:06
 * @Version 1.0
 */
@Controller
public class LoginController {
    @GetMapping("/login")
    public  String login(){
        return "login";
    }
    @GetMapping("/")
    public String home(){
        return "redirect:/index";
    }
    @GetMapping("/index")
    public String index(Model model){
        User user= (User) SecurityUtils.getSubject().getPrincipal();
        model.addAttribute("user",user);
        return "index";
    }
    @PostMapping("login")
    @ResponseBody
    public AjaxResult login(User user){
        System.out.println("user = " + user);
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
        //获取Subject 对象
        Subject subject= SecurityUtils.getSubject();
        try {
            subject.login(token);
            return AjaxResult.success("/index");
        } catch (UnknownAccountException e) {
            return AjaxResult.error(e.getMessage());
        } catch (IncorrectCredentialsException e) {
            return AjaxResult.error(e.getMessage());
        }
    }
}
6.登录页面
编写login.html页面
这里我只贴重要代码,具体的代码,到github里找哦!
<form id="loginForm">
    <input type="text" id="username" name="username" class="text"  />
    <input type="password" id="password" name="password"  />
</form>
<div class="signin">
    <input id="loginBut" type="button" value="Login" >
</div>
-------js代码----
<script type="text/javascript">
    $.fn.serializeObject = function () {
        var o = {};
        var a = this.serializeArray();
        $.each(a, function () {
            if (o[this.name]) {
                if (!o[this.name].push) {
                    o[this.name] = [o[this.name]];
                }
                o[this.name].push(this.value);
            } else {
                o[this.name] = this.value || '';
            }
        });
        return o;
    };
    $(function () {
        $("#loginBut").click(function () {
            var  arr=$('#loginForm').serializeObject();
            $.ajax({
                url: '/login',
                type: 'post',
                data:  arr,
                dataType: "json",
                success: function (data) {
                    if (data.code==200){
                        location.href=data.msg;
                    } else {
                        alert(data.msg);
                    }
                },
                error: function (data) {
                    alert(data.msg);
                }
            })
        });
    });
</script>
7.启动
先看一下目录

启动项目:访问http://localhost:8080/,它会自动拦截,页面重定向到 http://localhost:8080/login
总结:
SpringBoot整合Shiro实现用户认证功能就到此结束了,一些细节代码参见 https://github.com/Slags/springboot-learn/tree/master/1.springboot-shiro-authentication
附录:
1.Shiro拦截机制表
| Filter Name | Class | Description | 
|---|---|---|
| anon | org.apache.shiro.web.filter.authc.AnonymousFilter | 匿名拦截器,即不需要登录即可访问;一般用于静态资源过滤;示例/static/**=anon | 
| authc | org.apache.shiro.web.filter.authc.FormAuthenticationFilter | 基于表单的拦截器;如/**=authc,如果没有登录会跳到相应的登录页面登录 | 
| authcBasic | org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter | Basic HTTP身份验证拦截器 | 
| logout | org.apache.shiro.web.filter.authc.LogoutFilter | 退出拦截器,主要属性:redirectUrl:退出成功后重定向的地址(/),示例/logout=logout | 
| noSessionCreation | org.apache.shiro.web.filter.session.NoSessionCreationFilter | 不创建会话拦截器,调用subject.getSession(false)不会有什么问题,但是如果subject.getSession(true)将抛出DisabledSessionException异常 | 
| perms | org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter | 权限授权拦截器,验证用户是否拥有所有权限;属性和roles一样;示例/user/**=perms["user:create"] | 
| port | org.apache.shiro.web.filter.authz.PortFilter | 端口拦截器,主要属性port(80):可以通过的端口;示例/test= port[80],如果用户访问该页面是非80,将自动将请求端口改为80并重定向到该80端口,其他路径/参数等都一样 | 
| rest | org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter | rest风格拦截器,自动根据请求方法构建权限字符串;示例/users=rest[user],会自动拼出user:read,user:create,user:update,user:delete权限字符串进行权限匹配(所有都得匹配,isPermittedAll) | 
| roles | org.apache.shiro.web.filter.authz.RolesAuthorizationFilter | 角色授权拦截器,验证用户是否拥有所有角色;示例/admin/**=roles[admin] | 
| ssl | org.apache.shiro.web.filter.authz.SslFilter | SSL拦截器,只有请求协议是https才能通过;否则自动跳转会https端口443;其他和port拦截器一样; | 
| user | org.apache.shiro.web.filter.authc.UserFilter | 用户拦截器,用户已经身份验证/记住我登录的都可;示例/**=user | 
SpringBoot&Shiro实现用户认证的更多相关文章
- 项目一:第十一天 2、运单waybill快速录入  3、权限demo演示-了解 5、权限模块数据模型 6、基于shiro实现用户认证-登录(重点)
		
1. easyui DataGrid行编辑功能 2. 运单waybill快速录入 3. 权限demo演示-了解 4. Apache shiro安全框架概述 5. 权限模块数据模型 6. 基于shiro ...
 - Spring Cloud之路:(七)SpringBoot+Shiro实现登录认证和权限管理
		
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/sage_wang/article/details/79592269一.Shiro介绍1.Shiro是 ...
 - springboot shiro和freemarker集成之权限控制完全参考手册(跳过认证,登录由三方验证,全网首发)
		
本文主要考虑单点登录场景,登录由其他系统负责,业务子系统只使用shiro进行菜单和功能权限校验,登录信息通过token从redis取得,这样登录验证和授权就相互解耦了. 用户.角色.权限进行集中式管理 ...
 - spring-boot整合shiro作权限认证
		
spring-shiro属于轻量级权限框架,即使spring-security更新换代,市场上大多数企业还是选择shiro 废话不多说 引入pom文件 <!--shiro集成spring--& ...
 - springboot集成shiro实现权限认证
		
github:https://github.com/peterowang/shiro 基于上一篇:springboot集成shiro实现身份认证 1.加入UserController package ...
 - springboot系列(十)springboot整合shiro实现登录认证
		
关于shiro的概念和知识本篇不做详细介绍,但是shiro的概念还是需要做做功课的要不无法理解它的运作原理就无法理解使用shiro: 本篇主要讲解如何使用shiro实现登录认证,下篇讲解使用shiro ...
 - 用户认证授权和Shiro入门
		
1.权限管理基础(认证和授权): 前言 本文主要讲解的知识点有以下: 权限管理的基础知识 模型 粗粒度和细粒度的概念 回顾URL拦截的实现 Shiro的介绍与简单入门 一.Shiro基础知识 在学习S ...
 - springboot集成shiro实现身份认证
		
github地址:https://github.com/peterowang/shiro pom文件 <dependencies> <dependency> <group ...
 - SpringBoot+SpringSecurity之多模块用户认证授权同步
		
在之前的文章里介绍了SpringBoot和SpringSecurity如何继承.之后我们需要考虑另外一个问题:当前微服务化也已经是大型网站的趋势,当我们的项目采用微服务化架构时,往往会出现如下情况: ...
 
随机推荐
- TPO3-2Deletion of Ogallala Aquifer
			
In the face of the upcoming water supply crisis, a number of grandiose schemes have been developed t ...
 - Escape from the Hell
			
Escape from the Hell [JAG Asia 2016] 容易证明优先选择差值大的更优 对于最后一瓶我们可以枚举 枚举最后一瓶,然后在树状数组上消去它的影响,然后线段树check是否出 ...
 - 基础篇八:log配置
			
第一:首选查看有哪些日志文件 cd /etc/nginx/ cat nginx.conf cd /var/log/nginx/
 - 基础篇七:默认配置语法(/etc/nginx/nginx.conf)
			
首选我们 vim nginx.conf 参照上图,我们看看nginx.conf 的个参数含义 我们再看看 /etc/nginx/conf.d/default.conf
 - RHEL安装神器EPEL
			
什么是EPEL? EPEL的全称叫 Extra Packages for Enterprise Linux .EPEL是由 Fedora 社区打造,为 RHEL 及衍生发行版如 CentOS.Scie ...
 - mysql truncate 的问题
			
问题是微信群里一伙计提的 `mysql truncate 空表都需要3 4秒,要优化解决` 一开始觉得这莫名其妙,因为作这种操作的都是后台运维,不是实时的对外服务,运维又不差这3秒 其反应trunca ...
 - discount the possibility|pessimistic|bankrupt|
			
Nor can we discount the possibility that some factor in the diet itself has harmful effects. ADJ-GRA ...
 - [LC] 268. Missing Number
			
Given an array containing n distinct numbers taken from 0, 1, 2, ..., n, find the one that is missin ...
 - 线程、volatile与synchronized、Lock
			
目录 线程 1.概念: 2.线程生命周期: 3.线程调度 4.线程实现 4.1.实现方式 4.2.之间的区别: 5.线程安全 5.1.volatile与synchronized 5.1.synchro ...
 - 如何用Nginx解决跨域问题
			
一. 产生跨域的原因 1.浏览器限制 2.跨域 3.XHR(XMLHttpRequest)请求 二. 解决思路 解决跨域有多重,在这里主要讲用nginx解决跨域 1.JSONP 2.nginx代理 3 ...