集成shiro权限管理

Step 1. 添加依赖

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.4.0</version>
        </dependency>

Step 2. 权限类实体(ShiroUserDomain)

public class ShiroUserDomain implements Serializable {

    private Integer id;
    private String name;
    private String password;
    private String perms;
//省略GET、SET方法

Step 3. 添加DAO(ShiroUserDao)

@Mapper
public interface ShiroUserDao {

    @Select("select * from shiro_user where name = #{name}")
    public ShiroUserDomain findByName(String name);

    @Select("select * from shiro_user where id = #{id}")
    public ShiroUserDomain findById(Integer id);
}

Step 4. 添加Service

  • 添加Service接口类
public interface ShiroUserService {

    ShiroUserDomain findByName(String name);
    ShiroUserDomain findById(Integer id);
}
  • 添加Service实现类
@Service
public class ShiroUserServiceImpl implements ShiroUserService {

    @Autowired
    private ShiroUserDao shiroUserDao;

    @Override
    public ShiroUserDomain findByName(String name) {
        return shiroUserDao.findByName(name);
    }

    @Override
    public ShiroUserDomain findById(Integer id) {
        return shiroUserDao.findById(id);
    }
}

Step 5. 添加Realm

@Autowired
    private ShiroUserService shiroUserService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权逻辑");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Subject subject = SecurityUtils.getSubject();
        ShiroUserDomain user = (ShiroUserDomain) subject.getPrincipal();
        ShiroUserDomain dbUser = shiroUserService.findById(user.getId());
        info.addStringPermission(dbUser.getPerms());
        return info;

    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行认证逻辑");

        UsernamePasswordToken token  =  (UsernamePasswordToken)authenticationToken;

        ShiroUserDomain user = shiroUserService.findByName(token.getUsername());

        //1、判断用户名
        if(user == null){
            //用户名不存在
            return null;
            //shiro底层会抛出UnKnowAccountException
        }

        //2、判断密码, 这里的user是principal
        return new SimpleAuthenticationInfo(user,user.getPassword(),getName());
    }

Step 6. 添加ShiroConfig

@Configuration
public class ShiroConfig {
    @Autowired
    private ShiroUserService shiroUserService;

    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // 设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 添加Shiro内置过滤器
        /**
         * Shiro内置过滤器,可以实现权限相关的拦截器
         *  常用的过滤器:
         *      anon: 无需认证(登录)可以访问
         *      authc: 必须认证才可以访问
         *      user: 如果使用rememberMe功能可以直接访问
         *      perms: 该资源必须得到资源权限才可以访问
         *      role: 该资源必须得到角色权限才可以访问
         */

        Map<String, String> filerMap = new LinkedHashMap<>(); //顺序的map
        //配置记住我或认证通过可以访问的地址

        filerMap.put("/studentInfo","perms[user:student]");
        filerMap.put("/user/logout","authc");


        //设置登录的页面,发送toLogin请求
        shiroFilterFactoryBean.setLoginUrl("/");

        //设置未授权的页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");
        //设置过滤器
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filerMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 创建DefaultWebSecurityManager
     */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")ShiroUserRealm shiroUSerRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 关联realm
        securityManager.setRealm(shiroUSerRealm);
        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }

    /**
     * 创建Realm
     */
    @Bean(name = "userRealm")
    public ShiroUserRealm getRealm(){
        return new ShiroUserRealm();
    }

    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }

    /**
     2   * cookie对象;
     3   * rememberMeCookie()方法是设置Cookie的生成模版,比如cookie的name,cookie的有效时间等等。
     4   * @return
     5  */
    @Bean
    public SimpleCookie rememberMeCookie(){
        //System.out.println("ShiroConfiguration.rememberMeCookie()");
        //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //<!-- 记住我cookie生效时间30天 ,单位秒;-->
        simpleCookie.setMaxAge(259200);
        return simpleCookie;
    }

    /**
     * cookie管理对象;
     * rememberMeManager()方法是生成rememberMe管理器,而且要将这个rememberMe管理器设置到securityManager中
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager(){
        //System.out.println("ShiroConfiguration.rememberMeManager()");
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));
        return cookieRememberMeManager;
    }
}

Step 7. 编写Controller

  • LoginController
@Controller
@Api("登录相关接口")
public class LoginControll {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoginControll.class);

    @Autowired
    UserService userService;

    @ApiOperation(value = "登录", httpMethod = "POST", response = String.class)
    @PostMapping("/user/login")
    public String toLogin(
            HttpServletRequest request,
            HttpServletResponse response,
            @RequestParam(name = "username", required = true)
                    String username,
            @RequestParam(name = "password", required = true)
                    String password,
            Model model,
            HttpSession session
    ) {
//        String pwd = utils.MD5encode(username + password);
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);

        try {
            subject.login(token);
            session.setAttribute("loginUser", username);
            return "redirect:/main.html";
        } catch (UnknownAccountException e) {
            model.addAttribute("msg","用户名不存在");
            return "login";

        }catch (IncorrectCredentialsException e){
            model.addAttribute("msg","密码错误");
            return "login";
        }

    }

    @ApiOperation(value = "登出", httpMethod = "GET")
    @GetMapping(value = "/user/logout")
    public void toLogout(HttpServletRequest request,
                         HttpServletResponse response
    ) {
        request.getSession().removeAttribute("loginUser");
        try {
            response.sendRedirect("/");
        } catch (IOException e) {
            e.printStackTrace();
            LOGGER.error("注销失败", e);
        }
    }

    @ApiOperation(value = "无权限访问", httpMethod = "GET")
    @GetMapping("/noAuth")
    public String noAuth(){
        return "noAuth";
    }

    @GetMapping("/test")
    public String shouye(){
        return "login";
    }
}

Step 8. shiro权限控制的三种方式

  1. 编程方式:通过java代码
public String logon(Model model) {
        Subject subject = SecurityUtils.getSubject();

        if (subject.hasRole("xxx")) {
            return "logon";
        }
        model.addAttribute("msg","没有角色xxx");
        return "login";
    }
  1. JSP标签

<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.w3.org/1999/xhtml">

<shiro:authenticated> <p>登录之后</p></shiro:authenticated>   
<shiro:notAuthenticated> 未登录</shiro:notAuthenticated><input type="button" name="dianji" id="dianji"onclick="login()"/>
<shiro:hasRole name="manager">manager角色</shiro:hasRole>
<shiro:hasRole name="admin">我admin角色</shiro:hasRole>
<shiro:hasRole name="admins">我有admins角色  </shiro:hasRole>
<shiro:lacksRole name="user">没有user角色</shiro:lacksRole>
<shiro:hasPermission name="manager"><p>你有权限看到此处!</p></shiro:hasPermission>
  1. 通过注解方式:放在controller 方法头上(但是之前放在service可行,后来不行了,不知道为什么)
//shiro权限校验
//@RequiresRoles({"xcxcxc"})//角色校验
@RequiresPermissions({"xcxcxc"})//权限校验
@RequestMapping("/queryUser") 
@ResponseBody

建议别使用第三种方式,有很多问题。比如说:注解无效,影响其他注解@Cache无效,发生异常无法跳转等,很麻烦!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335