Puock主题已经正式发售,一款颜值在线的多功能WordPress主题,赶快戳我来看看吧

SpringBoot+Shiro整合进行登录验证与权限控制

18,101次阅读
22条评论

前言

最近在新写项目的时候,有需要用到大量的权限控制的地方,如果自己手动写太耗时了,所以就采用了Shiro进行权限控制与登录验证,下面我们来看看如何整合他们。 SpringBoot+Shiro整合进行登录验证与权限控制

正文

添加依赖

首先,我们先将maven依赖添加到项目的pom.xml中:

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

添加配置文件

然后添加Shiro配置,在Springboot中都是采用注解方式进行注册:

@Configuration
public class ShiroConfiguration {
    private Logger logger = LoggerFactory.getLogger(ShiroConfiguration.class);

    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
        logger.info("ShiroConfiguration initialized");
        ShiroFilterFactoryBean shiroFilterFactoryBean  = new ShiroFilterFactoryBean();

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

        //登出
        filterChainDefinitionMap.put("/logout", "logout");

        //<!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        filterChainDefinitionMap.put("/admin/**", "authc");
        filterChainDefinitionMap.put("/**", "anon");

        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }


    @Bean
    public SecurityManager securityManager(MyRealm myRealm){
        DefaultWebSecurityManager manager =  new DefaultWebSecurityManager();
        manager.setRealm(myRealm);
        return manager;
    }

    //开启shiro aop注解支持
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * DefaultAdvisorAutoProxyCreator,Spring的一个bean,由Advisor决定对哪些类的方法进行AOP代理。
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }


}

身份效验

接着在Shiro配置下新建一个MyRealm.java,继承AuthorizingRealm

public class MyRealm extends AuthorizingRealm {

    private final static Logger logger = LoggerFactory.getLogger(MyRealm.class);

    @Resource
    private UserService userService;
    @Resource
    private RoleService roleService;


    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        logger.debug("##################执行Shiro权限认证##################");
        User user = (User) principalCollection.getPrimaryPrincipal();
        if(user!=null){
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            Role role = roleService.getById(user.getRole().getRId());
            List<Permission> permissions = role.getPermissions();
            for (Permission p : permissions){
                info.addStringPermission(p.getPAlias());
            }
            info.addRole(role.getRAlias());
            return info;
        }
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        logger.info("用户验证执行 : "+token.getUsername());
        User user = userService.getByEmail(token.getUsername(),true);
        if(user==null){
            logger.error("用户 { "+token.getUsername()+" } 不存在 ");
            throw new AccountException("账户不存在");
        }
        if(user.getStatus()==0){
            logger.error("用户 { "+token.getUsername()+" } 被禁止登录 ");
            throw new DisabledAccountException("账号已经禁止登录");
        }else{
            user.setUpdated(DateUtils.getNowTimestamp());
            user.setUpdatedAt(DateUtils.getNowFormatDate(null));
            System.out.println("效验更新前ROLE:"+user.getRole().getRId());
            userService.update(user,true,user.getId());
        }
        return new SimpleAuthenticationInfo(user,user.getPassword(),getName());
    }


    @PostConstruct
    public void initCredentialsMatcher() {
        //该句作用是重写shiro的密码验证,让shiro用我自己的验证
        setCredentialsMatcher(new CredentialsMatcher());

    }
}
接着,我们还要添加一个密码校验器
public class CredentialsMatcher extends SimpleCredentialsMatcher {

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

    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        UsernamePasswordToken authcToken = (UsernamePasswordToken) token;
        Object tokenCredentials = EncryptUtils.md5(authcToken.getUsername()+String.valueOf(authcToken.getPassword()));
        Object accountCredentials = getCredentials(info);
        return accountCredentials.equals(tokenCredentials);
    }
}

[v_tips]我这里密码是采用用户名+密码进行MD5加密进行处理的,你可以根据自己的密码加密方式进行更改;到这一步,Shiro配置基本上都已经完成了。[/v_tips]

下面附上我的用户实体和权限、角色的实体以对应上方的身份验证。
@Data
public class User implements Serializable {
    /**
     * 用户ID
     */
    private Integer id;

    /**
     * 用户名
     */
    private String username;

    /**
     * 邮箱
     */
    private String email;

    /**
     * 手机号
     */
    private String phone;

    /**
     * 密码
     */
    private String password;

    /**
     * 角色
     */
    private Role role;

    /**
     * 状态:1-正常,0-封禁
     */
    private Integer status;

    /**
     * 头像
     */
    private String avatar;

    /**
     * 创建时间
     */
    private Long created;

    /**
     * 修改时间
     */
    private Long updated;

    /**
     * 创建时间字符串
     */
    private String createdAt;

    /**
     * 修改时间字符串
     */
    private String updatedAt;

    private static final long serialVersionUID = 1L;
}
@Data
public class Role implements Serializable {
    /**
     * 角色唯一标识符
     */
    private Integer rId;

    /**
     * 角色名称
     */
    @NotNull(message = "角色名称不能为空")
    private String rName;

    /**
     * 角色别名
     */
    @NotNull(message = "角色别名不能为空")
    private String rAlias;

    /**
     * 创建时间
     */
    private Long created;

    /**
     * 修改时间
     */
    private Long updated;

    /**
     * 创建时间字符串
     */
    private String createdAt;

    /**
     * 修改时间字符串
     */
    private String updatedAt;

    private List<Permission> permissions;


    private static final long serialVersionUID = 1L;


}
@Data
public class Permission implements Serializable {
    /**
     * 权限唯一标识ID
     */
    private Integer pId;

    /**
     * 权限名称
     */
    private String pName;

    /**
     * 权限别名
     */
    private String pAlias;

    /**
     * 创建时间
     */
    private Long created;

    /**
     * 修改时间
     */
    private Long updated;

    /**
     * 创建时间字符串
     */
    private String createdAt;

    /**
     * 修改时间字符串
     */
    private String updatedAt;

    private static final long serialVersionUID = 1L;
}
PS:在上述实体类代码中采用了lombok进行getter/setter方法的映射,所以如果要使用以上的实体类请添加lombok的依赖到项目中或者自己添加getter/setter方法 SpringBoot+Shiro整合进行登录验证与权限控制

29
憧憬Licoy
版权声明:本站原创文章,由憧憬Licoy于2017年09月10日发表,共计8701字。
转载提示:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(22条评论)
Loading...
未来 评论达人LV.1
2019-06-21 22:50:32 回复

楼主,主题是什么?能说下吗

Treasure 评论达人LV.1
2017-10-24 15:46:43 回复

在身份校验的时候,有这么一行代码:
User user = userService.getByEmail(token.getUsername(),true);
token.getUsername()拿到的类型不匹配,怎么办?不能强转

Treasure 评论达人LV.1
2017-10-24 14:57:50 回复

您的mapper和Dao,service以及controller可以给我看一下么?我纯粹的新手小白,还希望能多看一点萌发一下

Treasure 评论达人LV.1
2017-10-24 11:45:06 回复

刚刚回复为什么没有了,没别的,顶楼主,爱楼主,大爱楼主。通俗易懂,关键字都解释的特别到位,非常适合我这种启蒙小白,希望楼主多多写出这样的代码,给力!赞楼主!

    憧憬Licoy 博主
    2017-10-24 11:46:41 回复

    @Treasure 你刚才回复的实际上已经提交到后台了,但是因为页面是缓存了的,所以你刷新之后不会看你的评论,只有在审核通过之后才会自动刷新缓存,这个时候你才可以看见你的评论。

      Treasure 评论达人LV.1
      2017-10-24 11:57:38 回复

      @憧憬Licoy 楼主,你的这个写法,对Token运用的不是很多,而且没有验证码校验,你的实际项目能分享一下么?我看到的每一个shiro文章都是不同的实现写法,对于初学shiro的我,晕晕乎乎的

Treasure 评论达人LV.1
2017-10-24 11:43:14 回复

大爱楼主,这种每一行代码都有注释的文章,实在是我这shiro入门小白的爱,通俗易懂,好入门,关键字也解释到位!谢谢楼主分享,支持你的更多作品!

Amanda 评论达人LV.1
2017-09-30 17:41:40 回复

博主,你是做什么技术的呀 ? 是做什么语言开发的呀

快乐网讯 评论达人LV.1
2017-09-29 14:48:05 回复

文章不错非常喜欢

最好的代写essay服务 评论达人LV.1
2017-09-21 19:59:11 回复

看起来还是比较简单

夏日博客 评论达人LV.1
2017-09-10 23:31:59 回复

好复杂的权限控制。