springBoot整合shiro实现权限控制

1.pom.xml

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
    </parent>
<!--权限控制-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.3.2</version>
        </dependency>
        <!-- 页面使用shiro标签依赖 -->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>1.2.1</version>
        </dependency>

2.config

package com.xlt.xfzb.web.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.xlt.xfzb.web.security.UserRealm;
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.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * @Classname ShiroConfiguration
 * @Description 权限控制设置
 * @Date 2020/3/27 17:47
 * @Created by xm
 */
@Configuration
public class ShiroConfiguration {

    // 创建自定义 realm
    @Bean
    public UserRealm userRealm() {
        UserRealm userRealm = new UserRealm();
        return userRealm;
    }

    // 创建 SecurityManager 对象
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm());
        return securityManager;
    }

    // Filter工厂,设置对应的过滤条件和跳转条件
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        /**
         * anon:匿名用户可访问
         * authc:认证用户可访问
         * user:使用rememberMe可访问
         * perms:对应权限可访问
         * role:对应角色权限可访问
         */
        Map<String, String> map = new HashMap<>();
        // 开放登录接口
        map.put("/login", "anon");
        map.put("/error", "anon");
        map.put("/img/**","anon");
        map.put("/css/**","anon");
        map.put("/lib/**","anon");
        map.put("/js/**","anon");
        // 对所有用户认证
        map.put("/**", "authc");
        // 登出
        map.put("/logout", "logout");
        // 登录
        // 注意:这里配置的 /login 是指到 @RequestMapping(value="/login")中的 /login
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 首页
        shiroFilterFactoryBean.setSuccessUrl("/index");
        // 错误页面,认证不通过跳转
        shiroFilterFactoryBean.setUnauthorizedUrl("/error/unAuth");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

    // 加入注解的使用,不加这个,注解不生效
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    // 跟上面的注解配置搭配使用,有时候加了上面的配置后注解不生效,需要加入下面的配置
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator app = new DefaultAdvisorAutoProxyCreator();
        app.setProxyTargetClass(true);
        return app;
    }

    //页面标签对象
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
}

3.依赖实体

用户

package com.xlt.xfzb.web.entity;

import java.util.HashSet;
import java.util.Set;

/**
 * @Classname User
 * @Description 权限测试用户
 * @Date 2020/3/27 17:44
 * @Created by xm
 */
public class UserEntity {

    private String id;

    private String name;

    private String password;
    //账号
    private String account;
    //性别
    private String sex;
    //部门id
    private String deptId;
    //工号
    private String work;
    //自定义集合
    private Set<RoleEntity> roles = new HashSet<RoleEntity>();

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Set<RoleEntity> getRoles() {
        return roles;
    }

    public void setRoles(Set<RoleEntity> roles) {
        this.roles = roles;
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getDeptId() {
        return deptId;
    }

    public void setDeptId(String deptId) {
        this.deptId = deptId;
    }

    public String getWork() {
        return work;
    }

    public void setWork(String work) {
        this.work = work;
    }
}

角色

package com.xlt.xfzb.web.entity;

import java.util.HashSet;
import java.util.Set;

/**
 * @Classname Role
 * @Description TODO
 * @Date 2020/3/30 11:42
 * @Created by xm
 */
public class RoleEntity {

    private String id;
    //姓名
    private String name;
    //备注
    private String remark;
    //自定义菜单实体
    private Set<PermissionEntity> permissions = new HashSet<>();//一个角色有多个权限

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Set<PermissionEntity> getPermissions() {
        return permissions;
    }

    public void setPermissions(Set<PermissionEntity> permissions) {
        this.permissions = permissions;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }
}

权限

package com.xlt.xfzb.web.entity;

/**
 * @Classname Permission
 * @Description 权限实体
 * @Date 2020/3/30 11:41
 * @Created by xm
 */
public class PermissionEntity {

    private String id;

    private String name;
    //路径
    private String url;
    //描述
    private String descrirtion;
    //父id
    private String pid;


    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getDescrirtion() {
        return descrirtion;
    }

    public void setDescrirtion(String descrirtion) {
        this.descrirtion = descrirtion;
    }

    public String getPid() {
        return pid;
    }

    public void setPid(String pid) {
        this.pid = pid;
    }
}

4.自定义异常

package com.xlt.xfzb.web.security;

import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
 * @Classname NoPermissionException
 * @Description TODO
 * @Date 2020/3/31 11:16
 * @Created by xm
 */
@ControllerAdvice
public class NoPermissionException {

    // 授权失败,就是说没有该权限
    @ExceptionHandler(UnauthorizedException.class)
    public String handleShiroException(Exception ex) {
        return "/error/unAuth";
    }

    @ResponseBody
    @ExceptionHandler(AuthorizationException.class)
    public String AuthorizationException(Exception ex) {
        return "权限认证失败";
    }
}

5.权限认证

package com.xlt.xfzb.web.security;

import com.netflix.discovery.provider.Serializer;
import com.xlt.xfzb.web.entity.PermissionEntity;
import com.xlt.xfzb.web.entity.RoleEntity;
import com.xlt.xfzb.web.entity.UserEntity;
import com.xlt.xfzb.web.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.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

/**
 * @Classname UserRealm
 * @Description TODO
 * @Date 2020/3/31 11:02
 * @Created by xm
 */
@Service
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    /**
     * 用户授权
     **/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principalCollection) {

        System.out.println("===执行授权===");

        Subject subject = SecurityUtils.getSubject();
        UserEntity user = (UserEntity)subject.getPrincipal();
        if(user != null){
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            // 角色字符串集合
            Collection<String> rolesCollection = new HashSet<>();
            // 权限字符串集合
            Collection<String> premissionCollection = new HashSet<>();
            // 读取并赋值用户角色与权限
            Set<RoleEntity> roles = user.getRoles();
            for(RoleEntity role : roles){
                rolesCollection.add(role.getName());
                Set<PermissionEntity> permissions = role.getPermissions();
                for (PermissionEntity permission : permissions){
                    // 权限名称为PermissionEntity为字段url
                    premissionCollection.add(permission.getUrl());
                }
                info.addStringPermissions(premissionCollection);
            }
            info.addRoles(rolesCollection);
            return info;
        }
        return null;
    }

    /**
     * 用户认证
     **/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken authenticationToken) throws AuthenticationException {

        System.out.println("===执行认证===");

        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
        UserEntity bean = userService.findByName(token.getUsername());

        if(bean == null){
            // 用户不存在
            throw new UnknownAccountException();
        } else {
            bean = userService.findById(bean.getId());
            if(null == bean) {
                // 认证失败
                throw new AuthenticationException();
            }
        }

        ByteSource credentialsSalt = ByteSource.Util.bytes(bean.getName());

        return new SimpleAuthenticationInfo(bean, bean.getPassword(),
                credentialsSalt, getName());

    }
}

6.持久层

package com.xlt.xfzb.web.mapper;

import com.xlt.xfzb.web.entity.UserEntity;
import org.springframework.stereotype.Service;

/**
 * @Classname UserMapper
 * @Description 用户测试
 * @Date 2020/3/31 10:45
 * @Created by xm
 */
@Service
public interface UserMapper {

    // 根据用户名称,查询用户信息
     UserEntity findByName(String name);

    // 根据用户id,查询用户信息、角色、权限
     UserEntity findById(String id);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xlt.xfzb.web.mapper.UserMapper">

    <resultMap id="userMap" type="com.xlt.xfzb.web.entity.UserEntity">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="password" column="password"/>
        <collection property="roles" ofType="com.xlt.xfzb.web.entity.RoleEntity">
            <id property="id" column="roleId"/>
            <result property="name" column="roleName"/>
            <collection property="permissions" ofType="com.xlt.xfzb.web.entity.PermissionEntity">
                <id property="id" column="permissionId"/>
                <result property="name" column="permissionName"/>
                <result property="url" column="permissionUrl"/>
            </collection>
        </collection>
    </resultMap>

    <select id="findByName" parameterType="java.lang.String" resultType="com.xlt.xfzb.web.entity.UserEntity">
      SELECT "id", "name", "password"
        FROM "user"
          WHERE "name" = #{name}
    </select>

    <select id="findById" parameterType="java.lang.String" resultMap="userMap">
      SELECT u."id", u."name", u."password",
            r."id" as roleId, r."name" as roleName,
            p."id" as permissionId,
            p."name" as permissionName,
            p."url" as permissionUrl
        FROM "user" u , "user_role" ur, "role" r, "role_permission" rp, "permission" p
          WHERE u."id" = #{id}
            AND u."id" = ur."user_id"
            AND ur."role_id" = r."id"
            AND r."id" = rp."role_id"
            AND rp."permission_id" = p."id"
    </select>

</mapper>

7.service

package com.xlt.xfzb.web.service;

import com.xlt.xfzb.web.entity.UserEntity;

/**
 * @Classname UserService
 * @Description 用户相关业务接口
 * @Date 2020/3/30 11:48
 * @Created by xm
 */
public interface UserService {

    /**
     * 根据用户名称查询用户信息
     * @param name
     * @return
     */
    UserEntity findByName(String name);

    /**
     * 根据用户ID查询用户角色及权限
     * @param id
     * @return
     */
    UserEntity findById(String id);
}
package com.xlt.xfzb.web.service.impl;

import com.xlt.xfzb.web.entity.UserEntity;
import com.xlt.xfzb.web.mapper.UserMapper;
import com.xlt.xfzb.web.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @Classname UserService
 * @Description 用户相关业务接口实现
 * @Date 2020/3/30 11:48
 * @Created by xm
 */
@Service
public class UserServiceImpl implements UserService {

    //测试用户持久层
    @Autowired
    private UserMapper userMapper;

    /**
     * 根据用户名称查询用户信息
     * @param name
     * @return
     */
    @Override
    public UserEntity findByName(String name) {
        return userMapper.findByName(name);
    }

    /**
     * 根据用户ID查询用户角色及权限
     * @param id
     * @return
     */
    @Override
    public UserEntity findById(String id) {
        return userMapper.findById(id);
    }
}

8.controller

package com.xlt.xfzb.web.controller;


import com.xlt.xfzb.web.entity.UserEntity;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * @Classname MainController
 * @Description 主页验证前端控制器
 * @Date 2020/3/31 11:18
 * @Created by xm
 */
@Controller
public class MainController {

    private static final Logger log=LoggerFactory.getLogger(MainController.class);

    /**
     * 主页跳转
     * @param model 携带当前登录用户信息返回
     * @return
     */
    @RequestMapping("/index")
    public String index(Model model){
        UserEntity user = (UserEntity) SecurityUtils.getSubject().getPrincipal();
        model.addAttribute("uid",user.getId());
        model.addAttribute("uname",user.getName());
        log.info("当前登录用户-----"+user.getName());
        return "index";
    }

    /**
     * 登录接口+登录页面
     * @param request
     * @param response
     * @return
     */
    @RequestMapping("/login")
    public String login(HttpServletRequest request, HttpServletResponse response){
        response.setHeader("root", request.getContextPath());
        String userName = request.getParameter("username");
        String password = request.getParameter("password");
        // 等于null说明用户没有登录,只是拦截所有请求到这里,那就直接让用户去登录页面,就不认证了。
        // 如果这里不处理,那个会返回用户名不存在,逻辑上不合理,用户还没登录怎么就用户名不存在?
        if(null == userName) {
            return "login";
        }

        // 1.获取Subject
        Subject subject = SecurityUtils.getSubject();
        // 2.封装用户数据
        UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
        // 3.执行登录方法
        try{
            subject.login(token);
            return "redirect:/index";
        } catch (UnknownAccountException e){
            // 这里是捕获自定义Realm的用户名不存在异常
            log.info("用户名不存在!");
        } catch (IncorrectCredentialsException e){
            log.info("密码错误!");
        } catch (AuthenticationException e) {
            log.info("认证失败!");
        }

        return "login";
    }

    /**
     * 用户登出操作
     * @return
     */
    @RequestMapping("/logout")
    public String logout(){
        Subject subject = SecurityUtils.getSubject();
        if (subject != null) {
            subject.logout();
        }
        return "login";
    }

    /**
     * 错误页面
     * @return
     */
    @RequestMapping("/error/unAuth")
    public String unAuth(){
        return "/error/unAuth";
    }

    @RequestMapping("/err")
    public String err(){
        return "/error/unAuth";
    }


}

9.登录页面 login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<h1>用户登录</h1>
<hr>
<form id="from" action="/login" method="post">
    <table>
        <tr>
            <td>用户名</td>
            <td>
                <input type="text" name="username" placeholder="请输入账户名" value="" />
            </td>
        </tr>
        <tr>
            <td>密码</td>
            <td>
                <input type="password" name="password" placeholder="请输入密码"/>
            </td>
        </tr>
        <tr>
            <td colspan="2">
                <input type="submit" value="登录"/>
                <input type="reset" value="重置"/>
            </td>
        </tr>
    </table>
</form>
</body>
</html>

10.首页 index.HTML

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">

<head>
    <title>首页</title>
</head>
<body>
<h1>首页</h1>
<hr>
<ul>
    <li><a href="user/index" >个人中心</a></li>
    <li><a href="vip/index">会员中心</a></li>
    <p th:text="'用户编号:' + ${uid}"/>
    <p th:text="'用户姓名:' + ${uname}"/>
    <shiro:hasPermission name="user1"><h2>admin</h2></shiro:hasPermission>
    <shiro:hasPermission name="vip1"><h2>vip</h2></shiro:hasPermission>
    <shiro:hasPermission name="svip1"><h2>admin</h2></shiro:hasPermission>
    <li><a href="logout">退出登录</a></li>
</ul>
</body>
</html>

11.数据库结构

user 

 

role

 

user_role

 

permission

 

 role_permission

 12.  页面测试

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值