springboot整合shiro代码实现

pom依赖

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--shiro的jar包-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.2.2</version>
        </dependency>
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
        <dependency>
            <groupId>net.sourceforge.nekohtml</groupId>
            <artifactId>nekohtml</artifactId>
            <version>1.9.22</version>
        </dependency>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
        <!--thymeleaf 页面配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

自定义shiro的安全管理器配置

package com.jk.shiro;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * shiro的配置文件类 相当与之前的spring.xml配置文件
 */
@Configuration//声明当前类是一个spring配置文件类
public class ShiroConfig {
    //@Bean 相当于 <bean name="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor">
    // </bean>
    //LifecycleBeanPostProcessor将Initializable和Destroyable的实现类统一在其内部自动分别调用了
    // Initializable.init()和Destroyable.destroy()方法,从而达到管理shiro bean生命周期的目的
    @Bean(name = "lifecycleBeanPostProcessor")//shiro和spring bean之间的结合类
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
    //shiro 安全管理器 shiro的主入口
    @Bean
    public SecurityManager securityManager() {

        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        // 设置realm.
        // 注入缓存管理器;
        //securityManager.setCacheManager(ehCacheManager());
          //在安全管理器中 注入realm.数据源
          securityManager.setRealm(myShiroRealm());
        //securityManager.setRememberMeManager(rememberMeManager());

        return securityManager;

    }

    //shiro的主要核心过滤器
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {


        //创建一个shiro过滤器工厂
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 拦截器.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();

        // 配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
        //shiro过滤器链 按照过滤器链顺序执行过滤内容
        //logout 退出登录 /logout当前项目中的退出路径  logout是shiro的退出登录过滤器
        //当页面访问退出登录路径时 会进入shiro退出登录拦截器 自动退出登录
        filterChainDefinitionMap.put("/logout", "logout");
        //anon  放过权限拦截过滤器 不需要权限也能访问的资源
        filterChainDefinitionMap.put("/login/**", "anon");
        filterChainDefinitionMap.put("/gifCode", "anon");
        //toLogin 跳转到登录页面请求
        filterChainDefinitionMap.put("/login", "anon");
        //除上面放过拦截的路径之外 其余路径都会被拦截
        // authc权限拦截过滤器 会走认证和授权 查看当前认证有无通过 是否有权限访问
        filterChainDefinitionMap.put("/**", "authc");
        //LoginUrl 设置登录请求路径 访问此路径 会进入shiro的认证器方法 根据页面传递的用户名密码进行认证
        //当认证失败之后还会调用此路径 调用Controller中的login方法 提示用户 用户名错误还是密码错误
        shiroFilterFactoryBean.setLoginUrl("/toLogin");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/toIndex");
        // 未授权界面;
        //shiroFilterFactoryBean.setUnauthorizedUrl("/warning");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        System.out.println("注入成功");
        return shiroFilterFactoryBean;

    }
    //注入数据源 (认证、授权类)
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")//@DependsOn保证lifecycleBeanPostProcessor在它之前执行
    public MyRealm myShiroRealm(){
        MyRealm myShiroRealm = new MyRealm();
        return myShiroRealm;
    }

    //为当前shiro配置aop切面 使shiro与springAOP结合
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    //设置当前aop动态代理为cglib代理
    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
        //ProxyTargetClass 代理目标(实例) 类 把当前代理目标设置为类代理 默认为interface接口代理
        daap.setProxyTargetClass(true);
        return daap;
    }

    //thymlef和shiro整合时需要注入此类来完成权限验证
    @Bean
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }

}

配置realm,认证和授权,继承AuthorizingRealm类,实现AuthorizationInfo授权和AuthenticationInfo

package com.jk.shiro;

import com.jk.model.Menu;
import com.jk.model.User;
import com.jk.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class MyRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;
    //授权器
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //User user = (User) principals.getPrimaryPrincipal();
        User user = (User) SecurityUtils.getSubject().getPrincipal();
        System.out.println(user.getId());
        //创建一个简单的授权器
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //获取用户角色集
        List<String> roleList = userService.getRoleByUserId(user.getId());
        Set<String> set = new HashSet<>(roleList);
        simpleAuthorizationInfo.setRoles(set);

        // 获取用户权限集
        List<Menu> permissionList = userService.getUserMenuAll(user.getId());
        List<String> permissions = new ArrayList<>();
        for (Menu menu: permissionList) {
            // 处理用户多权限 用逗号分隔
            if (StringUtils.isEmpty(menu.getPerms())){
                continue;
            }
            permissions.add(menu.getPerms());
            System.out.println(menu.getPerms());
        }
        simpleAuthorizationInfo.addStringPermissions(permissions);
        return simpleAuthorizationInfo;
    }


    //认证器 登录
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //token 令牌  token.getPrincipal() 获取到前台页面输入的用户名
        // 获取用户输入的用户名和密码
        String userName = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());
        User user = userService.getUserByName(userName);
        if (user == null) {
            throw new UnknownAccountException("用户名或密码错误!");
        }
        if (!password.equals(user.getPassword())) {
            throw new IncorrectCredentialsException("用户名或密码错误!");
        }
        //创建一个简单认证器   第一个参数为当前登录用户的主体 可以是用户名 也可以是用户对象 一般都是用户对象
        //第二个参数为数据库查询出来的密码 认证器会用前台传递的密码后查询出来的密码对比
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
        //super.clearCachedAuthorizationInfo(simpleAuthenticationInfo.getPrincipals());
       // SecurityUtils.getSubject().getSession().setAttribute("login",user);
        //MD5 加密+加盐+多次加密
        //SimpleAuthenticationInfo authcInfo = new SimpleAuthenticationInfo(adminUser, password,ByteSource.Util.bytes(salt), this.getName());
        return simpleAuthenticationInfo;
    }
}

控制层登录调用的方法

//这里ResponseBo是自定义返回类,类似Map
    @PostMapping("/login")
    @ResponseBody
    public ResponseBo login(User user, String code, Boolean rememberMe,HttpServletRequest request) {
        if (StringUtils.isEmpty(code)) {
            return ResponseBo.warn("验证码不能为空!");
        }
        HttpSession session = request.getSession();
        String sessionCode = (String) session.getAttribute(CODE_KEY);
        if (!code.equalsIgnoreCase(sessionCode)) {
            return ResponseBo.warn("验证码错误!");
        }

        String password = user.getPassword();
        // 密码 MD5 加密
        //password = MD5Utils.encrypt(user.getLoginNumber().toLowerCase(), password);
        UsernamePasswordToken token = new UsernamePasswordToken(user.getLoginNumber(), password, rememberMe);
        try {
            Subject subject = getSubject();
            if (subject != null)
                subject.logout();
            subject.login(token);//调用此方法会进认证器进行认证
            //this.userService.updateLoginTime(username);
            return ResponseBo.ok();
        } catch (UnknownAccountException | IncorrectCredentialsException | LockedAccountException e) {
            return ResponseBo.error(e.getMessage());
        } catch (AuthenticationException e) {
            return ResponseBo.error("认证失败!");
        }
    }

数据库的配置结构:

在类上加@@RequiresPermissions("数据库定义的权限标识名")

@RequestMapping("user/getUser")
    @ResponseBody
    @RequiresPermissions("user:list")//shiro授权基于aop实现
    public List<User> getUser(){
        List<User> userList = new ArrayList<>();
        try {
            userList = userService.getUser();
        }catch(AuthorizationException e){

        }
        return userList;
    }

页面按钮加shiro:hasPermission="数据库定义的按钮标识名"

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/login/js/easyui/themes/icon.css"/>
    <link rel="stylesheet" href="/login/js/easyui/themes/default/easyui.css"/>
    <link rel="stylesheet" href="/login/js/easyui/themes/color.css"/>
    <script src="/login/js/jquery.min.js"></script>
    <script src="/login/js/easyui/jquery.easyui.min.js"></script>
    <script src="/login/js/easyui/locale/easyui-lang-zh_CN.js"></script>
</head>
<body>
    <!--hasPermission 判断是否拥有权限,拥有则显示html标签,未拥有则不展示-->


    <div id="userToolbar">
        <a shiro:hasPermission="user:add" >新增</a>
        <a shiro:hasPermission="user:update" href="#">修改</a>
        <a shiro:hasPermission="user:delete" href="#">删除</a>
        <a shiro:hasPermission="user:delete" href="javascript:logout()">注销</a>
    </div>
    <table id="userTable"></table>
<script>
    function addUser(aa){

    }
    function logout(){
        location.href="../logout";
    }
    function loadList(){
        $("#userTable").datagrid({
            url:"/user/getUser",
            toolbar:"#userToolbar",
            columns:[
                [
                {field:"id",title:"ID"},
                {field:"name",title:"ID"},
                {field:"loginNumber",title:"ID"},
                {field:"password",title:"ID"},
                {field:"cc",title:"操作"},
            ]
            ]
        })
    }
    $(function(){
        loadList();
    })

</script>
</body>
</html>

ok,大功告成。

猜你喜欢

转载自blog.csdn.net/weixin_42140580/article/details/85204379
今日推荐