springboot集成shiro基于注解权限控制,remember me功能

1.依赖

 <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>

2.数据库三张表:用户角色权限

-- auto-generated definition
create table t_user
(
  id       int auto_increment
  comment '用户主键'
    primary key,
  username varchar(20) not null
  comment '用户名',
  password varchar(20) not null
  comment '密码',
  role_id  int         null
  comment '关联role表'
)

-- auto-generated definition
create table t_role
(
  id       int auto_increment
  comment '主键'
    primary key,
  rolename varchar(20) null
  comment '角色名称'
)
  charset = utf8;

-- auto-generated definition
create table t_permission
(
  id             int auto_increment
  comment '主键'
    primary key,
  permissionname varchar(50) not null  
  comment '权限名,如/user/teacher',
  role_id        int         null
  comment '关联role'
)
  charset = utf8;

3.自定义ream

public class CustomRealm extends AuthorizingRealm {

        @Autowired
        private UserMapper userMapper;

        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            // 获取用户会话对象,即登录认证的
            User user = (User) principalCollection.getPrimaryPrincipal();
            SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
            //根据用户名或id从数据库/redis读取用户角色与权限进行权限校验
            authorizationInfo.setRoles(userMapper.getRoles(user.getUserName()));
            authorizationInfo.setStringPermissions(userMapper.getPermissions(user.getUserName()));
            return authorizationInfo;
        }
        
        //登录认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            String userName = (String) authenticationToken.getPrincipal();
            User user = userMapper.getByUsername(userName);
            //传入要本地session或redis存储的会话对象user,第二个为要与登录入参密码比对的user表用户密码
            return user==null?null:new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());

        }
    }

4.几个测试接口

@RestController
@RequestMapping("/user")
public class HomeController {


    @PostMapping("/login")
    public String login(@RequestBody User user, HttpServletRequest request) {
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), user.getPassword(),user.isRememberMe());
        Subject subject = SecurityUtils.getSubject();
        try{
            subject.login(token);  // 开始认证,这一步会跳到我们自定义的 Realm 中
            return "success";
        }catch(Exception e){
            e.printStackTrace();
            request.setAttribute("error", "用户名或密码错误!");
            return "login";
        }
    }

        /**
         * 身份认证测试接口
         * @param request
         * @return
         */
        @RequestMapping("/admin")
        public String admin(HttpServletRequest request) {
            return "success";
        }

        /**
         * 角色认证测试接口
         * @param request
         * @return
         */
        @RequiresRoles(value = "student")
        @RequestMapping("/student")
        public String student(HttpServletRequest request) {
            return "success";
        }

        /**
         * 权限认证测试接口
         * @param request
         * @return
         */
        @RequiresPermissions(value = "/user/teacher")
        @RequestMapping("/teacher")
        public String teacher(HttpServletRequest request) {
            return "success";
        }

}

5.shiro核心配置


@Configuration
@Slf4j
public class ShiroConfig {

        //shiro过滤器配置
        @Bean
        public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            // 设置默认登录的 URL,身份认证失败会访问该 URL
            shiroFilterFactoryBean.setLoginUrl("/user/login");
            // 设置成功之后要跳转的链接
            shiroFilterFactoryBean.setSuccessUrl("/success");
            // 设置未授权界面,权限认证失败会访问该URL
            shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
            // LinkedHashMap 是有序的,进行顺序拦截器配置
            Map<String,String> filterChainMap = new LinkedHashMap<>();
            // 配置可以匿名访问的地址,可以根据实际情况自己添加,放行一些静态资源等,anon 表示放行
            filterChainMap.put("/css/**", "anon");
            filterChainMap.put("/imgs/**", "anon");
            filterChainMap.put("/js/**", "anon");
            filterChainMap.put("/swagger-*/**", "anon");
            filterChainMap.put("/swagger-ui.html/**", "anon");
            filterChainMap.put("/login", "anon");
            // 具有“admin”角色才允许,一般在接口使用@RequiresRoles配置
            //filterChainMap.put("/user/student*/**", "roles[admin]");
            // 具有“user:create”权限才允许一般在接口使用@RequiresPermissions配置
            // filterChainMap.put("/user/teacher*/**", "perms[\"user:create\"]");
            // 配置 logout 过滤器
            filterChainMap.put("/logout", "logout");
            //没匹配上前面过滤器链中任何过滤器,最后都走authc过滤器,即必须登录
            filterChainMap.put("/**", "authc");
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
            return shiroFilterFactoryBean;
        }



    //shiro核心bean
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
        defaultSecurityManager.setRealm(customRealm());
        defaultSecurityManager.setRememberMeManager(getCookieRememberMeManager());
        //集群部署时需要自定义redis保存会话信息
        //defaultSecurityManager.setCacheManager();
        //defaultSecurityManager.setSessionManager();
        return defaultSecurityManager;
    }

    @Bean
    public CustomRealm customRealm() {
        return new CustomRealm();
    }

    //首先在登录页面选中RememberMe然后/login接口登录成功,浏览器会把名为RememberMe的Cookie写到客户端并保存下来,重新打开浏览器还能正常调用各接口不用重新登录;是否热rememberMe有用户登录传入参数,并在登录接口中UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), user.getPassword(),user.isRememberMe());进行设置;
如下两个bean配置rememberMe功能的cookie,不配置默认有效期1d
    @Bean
    public CookieRememberMeManager getCookieRememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(getSimpleCookie());
        cookieRememberMeManager.setCipherKey(Base64.decode("Z3VucwAAAAAAAAAAAAAAAA=="));
        return cookieRememberMeManager;
    }
    @Bean
    public SimpleCookie getSimpleCookie() {
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setHttpOnly(true);
        cookie.setMaxAge(7 * 24 * 60 * 60);//7天
        return cookie;
    }


    //开启权限shiro注解 @RequiresRoles @RequiresPermissions,aop实现的
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
    
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }


}
public class RequestUtils {
    
    /**
     * 获取当前登录的用户,若用户未登录,则返回未登录 json
     * @return
     */
    public static SysUser currentLoginUser() {
        Subject subject = SecurityUtils.getSubject();
        if (subject.isAuthenticated()) {
            Object principal = subject.getPrincipals().getPrimaryPrincipal();
            if (principal instanceof SysUser) {
                return (SysUser) principal;
            }
        }
        return null;
    }
    
}

 

测试注意:shiro依赖cookie-session机制实现的,需要使用浏览器进行测试,postman等工具不保存jsessionid,会导致每次访问不能根据jsessionid找到上次请求的session信息。

使用感触:感觉还是挺麻烦,单机系统前后端不分离如一些后台管理系统等可能比较适合,分布式集群系统不如直接jwt保存用户session(当然集群部署时shiro是可以结合redis实现session共享的),自定义注解+aop+redis实现权限控制灵活,易于理解。

参考文章:https://blog.csdn.net/taojin12/article/details/88343990?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值