SpringBoot整合Shiro(实现简单登录权限管理)

首先上几张图来看看今天要完成什么任务

1.需要实现的功能

登录拦截 任何请求都会拦截到登录页面(拦截器)
1

2.验证用户名密码是否正确(Shiro)
2

2.什么是Shiro?了解Shiro

Shiro是Apache下的一个开源项目。shiro属于轻量级框架,相对于SpringSecurity简单的多,也没有SpringSecurity那么复杂。
简单的来说Shiro是一个Java Web安全验证框架,可以进行验证授权等操作十分的简单方便所以深受大家喜欢。

shiro主要有三大功能模块:

  1. Subject:主体,一般指用户。
  2. SecurityManager:安全管理器,管理所有Subject,可以配合内部安全组件。(类似于SpringMVC中的DispatcherServlet)
  3. Realm:用于进行权限信息的验证,一般需要自己实现。

shiro实现原理理解
4
如图所示一个简单的Shiro控制就是利用代码通过Subject来进行认证和授权,而Subject又委托给 SecurityManager;我们需要给Shiro的SecurityManager注入 Realm,从而让 SecurityManager 能得到合法的用户及其权限进行判断。

3.通过项目学习(简单实现):

首先我们的项目结构
5

在pom.xml添加Shiro依赖

 <!--整合shiro-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.5.0</version>
        </dependency>

导入静态资源,和Thymeleaf等准备工作
正式开始操作
首先我们先解决第一个任务:登录拦截 任何请求都会拦截到登录页面(拦截器)
LoginHanderInterceptor.java

/**
 * 拦截器在Controller处理
 *
 * @Author MRyan
 * @Date 2020/2/15 11:03
 * @Version 1.0
 */
public class LoginHanderInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object loginUser = request.getSession().getAttribute("loginUser");//获取session对象
        if (loginUser == null) {
            request.setAttribute("msg", "没有权限,请先登录");
            request.getRequestDispatcher("/login.html").forward(request, response);//可以用来把当前request传递到该资源,参数为请求地址 或者把新的资源包括到当前响应中 类似model 传递给login.html
            return false;
        } else {
            return true;
        }

    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

当我们启动项目的时候任何操作都会被HandlerInterceptor所处理,通过session检测到我们未登录后进行拦截并将拦截信息传递给login.html页面展示

将自定义拦截类注册到WebMvcConfigurer 并托管给Springboot
MyMvcConfig.java

/**
 * @Author MRyan
 * @Date 2020/2/15 10:47
 * @Version 1.0
 */
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    /**
     * 自定义视图解释器
     *
     * @param registry
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //访问任何请求跳转都到登录页面
        registry.addViewController("/").setViewName("login");
        registry.addViewController("/main.html").setViewName("index");
        registry.addViewController("/login.html").setViewName("login");//这个地方需要注意 一定要有目的是跳转登录请求
    }
    /**
     * 添加自定义拦截器
     *
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginHanderInterceptor()).addPathPatterns("/**").
                excludePathPatterns("/",
                        "/login.html",//不要忘记把index.html排除拦截范围
                        "/css/**",
                        "/js/**",
                        "/noauth",
                        "/imgs/**",
                        "/index",
                        "/user/**")
               ;//拦截所有请求路径除了 login请求路径 定义静态资源路径
    }
}

由此实现了任何请求都会跳转到登录页面并将拦截信息展示交互

接下来我们来实现第二个任务:验证用户名密码是否正确(Shiro)
首先我们自定义MyShiroConfig
其实这就是死代码,代码编写从下至上
//ShiroFilterFactoryBean 通过一个ShiroFilter入口来拦截需要安全控制的URL,然后进行相应的控制。
//DefaultWebSecurityManager //设置关联Realm
// UserRealm 进行一些授权认证操作

MyShiroConfig.java

/**
 * @Author MRyan
 * @Date 2020/2/15 12:34
 * @Version 1.0
 */
@Configuration//托管给Springboot
public class MyShiroConfig {
    //ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("DefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);
        //添加Shiro内置过滤器
        /*
         anon:无需认证就可以访问
        authc:必须认证了才能访问
        user:必须拥有 记住我  功能才能访问
        perms:拥有对某个资源的权限才能访问
        role:拥有某个角色权限才能访问
        * */
        //拦截
        //授权 正常情况下,没有授权会跳到未授权页面
        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/main.html", "perms[user:root]");//设置首页必须认证才能访问 并赋予权限


        bean.setFilterChainDefinitionMap(filterMap);

        //设置登录的请求
        bean.setLoginUrl("/login");
        //设置未授权跳转请求
        bean.setUnauthorizedUrl("/noauth");

        return bean;
    }


    //DefaultWebSecurityManager
    @Bean("DefaultWebSecurityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("UserRealm") UserRealm userRealm) {//@Qualifier 选择和参数中同名的bean
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //设置关联Realm
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    // UserRealm
    @Bean("UserRealm")
    public UserRealm getRealm() {
        return new UserRealm();
    }


}

UserRealm.java

/**
 * @Author MRyan
 * @Date 2020/2/15 12:40
 * @Version 1.0
 */
public class UserRealm extends AuthorizingRealm {

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
      //在这里进行一些授权 分发权限等等
      SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
      Subject subject = SecurityUtils.getSubject();
      User user = (User) subject.getPrincipal();//接受AuthenticationInfo传递过来的User对象
      /* System.out.println(user.getShiro());*/
      info.addStringPermission(user.getShiro());//设置权限
      return info;
    }


    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
     //模拟数据库中获取数据
    User user = new User();
    user.setUsername("root");
    user.setPassword("123456");
    user.setShiro("user:root");
    UsernamePasswordToken usertoken = (UsernamePasswordToken) token;//全局关系

    if (!usertoken.getUsername().equals(user.getUsername())) {
        return null;//跑出异常给LoginController
    }
    return new SimpleAuthenticationInfo(user, user.getPassword(), "");//Shrio自己做加密  第一个参数传递 
user对象 第二个参数传递密码
    }
}

接下来是登录Controller

/**
 * @Author MRyan
 * @Date 2020/2/15 13:29
 * @Version 1.0
 */
@Controller
public class LoginController {


    @GetMapping("/user/login")
    public String login(String username, String password,
                        Model model) {//简单实验 未做加密 post请求 捏造数据模拟数据库登录
        //获取当前的用户
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
        try {
            subject.login(usernamePasswordToken);//登录成功 返回首页
            return "redirect:/main.html";
        } catch (UnknownAccountException e) {//抛出异常用户名不存在
            model.addAttribute("msg", "用户名不存在");
            return "login";
        } catch (IncorrectCredentialsException e) {//抛出异常密码不存在
            model.addAttribute("msg", "密码错误");
            return "login";
        }
    }

}

由此可见Shiro的强大之处了,很神奇将配置实现连接起来,我们什么都不用管都交给Shiro即可。

4.成果:

接下来开启项目看效果吧
5

猜你喜欢

转载自blog.csdn.net/qq_35416214/article/details/106207727