shiro java配置,SpringBoot+shiro整合学习之登录认证和权限控制

学习任务目标

用户必须要登陆之后才能访问定义链接,否则跳转到登录页面。

对链接进行权限控制,只有当当前登录用户有这个链接访问权限才可以访问,否则跳转到指定页面。

输入错误密码用户名或则用户被设置为静止登录,返回相应json串信息。

我是用的是之前搭建的一个springboot+mybatisplus+jsp的一个基础框架。在这之上进行shiro的整合。需要的同学可以去我的码云下载。

导入shiro依赖包到pom.xml

org.apache.shiro

shiro-spring

1.3.2

采用RBAC模式建立数据库

RBAC 是基于角色的访问控制(Role-Based Access Control )在 RBAC 中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。

/*表结构插入*/

DROP TABLE IF EXISTS `u_permission`;

CREATE TABLE `u_permission` (

`id` bigint(20) NOT NULL AUTO_INCREMENT,

`url` varchar(256) DEFAULT NULL COMMENT 'url地址',

`name` varchar(64) DEFAULT NULL COMMENT 'url描述',

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8;

/*Table structure for table `u_role` */

DROP TABLE IF EXISTS `u_role`;

CREATE TABLE `u_role` (

`id` bigint(20) NOT NULL AUTO_INCREMENT,

`name` varchar(32) DEFAULT NULL COMMENT '角色名称',

`type` varchar(10) DEFAULT NULL COMMENT '角色类型',

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

/*Table structure for table `u_role_permission` */

DROP TABLE IF EXISTS `u_role_permission`;

CREATE TABLE `u_role_permission` (

`rid` bigint(20) DEFAULT NULL COMMENT '角色ID',

`pid` bigint(20) DEFAULT NULL COMMENT '权限ID'

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*Table structure for table `u_user` */

DROP TABLE IF EXISTS `u_user`;

CREATE TABLE `u_user` (

`id` bigint(20) NOT NULL AUTO_INCREMENT,

`nickname` varchar(20) DEFAULT NULL COMMENT '用户昵称',

`email` varchar(128) DEFAULT NULL COMMENT '邮箱|登录帐号',

`pswd` varchar(32) DEFAULT NULL COMMENT '密码',

`create_time` datetime DEFAULT NULL COMMENT '创建时间',

`last_login_time` datetime DEFAULT NULL COMMENT '最后登录时间',

`status` bigint(1) DEFAULT '1' COMMENT '1:有效,0:禁止登录',

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;

/*Table structure for table `u_user_role` */

DROP TABLE IF EXISTS `u_user_role`;

CREATE TABLE `u_user_role` (

`uid` bigint(20) DEFAULT NULL COMMENT '用户ID',

`rid` bigint(20) DEFAULT NULL COMMENT '角色ID'

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Dao层代码的编写

Dao层的entity,service,mapper等我是采用mybatisplus的代码自动生成工具生成的,具备了单表的增删改查功能和分页功能,比较方便,这里我就不贴代码了。

配置shiro

ShiroConfig.java

/**

* @author 作者 z77z

* @date 创建时间:2017年2月10日 下午1:16:38

*

*/

@Configuration

public class ShiroConfig {

/**

* ShiroFilterFactoryBean 处理拦截资源文件问题。

* 注意:单独一个ShiroFilterFactoryBean配置是或报错的,以为在

* 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager

*

* Filter Chain定义说明 1、一个URL可以配置多个Filter,使用逗号分隔 2、当设置多个过滤器时,全部验证通过,才视为通过

* 3、部分过滤器可指定参数,如perms,roles

*

*/

@Bean

public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {

ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

// 必须设置 SecurityManager

shiroFilterFactoryBean.setSecurityManager(securityManager);

// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面

shiroFilterFactoryBean.setLoginUrl("/login");

// 登录成功后要跳转的链接

shiroFilterFactoryBean.setSuccessUrl("/index");

// 未授权界面;

shiroFilterFactoryBean.setUnauthorizedUrl("/403");

// 拦截器.

Map filterChainDefinitionMap = new LinkedHashMap();

// 配置不会被拦截的链接 顺序判断

filterChainDefinitionMap.put("/static/**", "anon");

filterChainDefinitionMap.put("/ajaxLogin", "anon");

// 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了

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

filterChainDefinitionMap.put("/add", "perms[权限添加]");

// :这是一个坑呢,一不小心代码就不好使了;

//

filterChainDefinitionMap.put("/**", "authc");

shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

System.out.println("Shiro拦截器工厂类注入成功");

return shiroFilterFactoryBean;

}

@Bean

public SecurityManager securityManager() {

DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

// 设置realm.

securityManager.setRealm(myShiroRealm());

return securityManager;

}

/**

* 身份认证realm; (这个需要自己写,账号密码校验;权限等)

*

* @return

*/

@Bean

public MyShiroRealm myShiroRealm() {

MyShiroRealm myShiroRealm = new MyShiroRealm();

return myShiroRealm;

}

}

登录认证实现

在认证、授权内部实现机制中都有提到,最终处理都将交给Real进行处理。因为在Shiro中,最终是通过Realm来获取应用程序中的用户、角色及权限信息的。通常情况下,在Realm中会直接从我们的数据源中获取Shiro需要的验证信息。可以说,Realm是专用于安全框架的DAO.

Shiro的认证过程最终会交由Realm执行,这时会调用Realm的getAuthenticationInfo(token)方法。

该方法主要执行以下操作:

1、检查提交的进行认证的令牌信息

2、根据令牌信息从数据源(通常为数据库)中获取用户信息

3、对用户信息进行匹配验证。

4、验证通过将返回一个封装了用户信息的AuthenticationInfo实例。

5、验证失败则抛出AuthenticationException异常信息。

而在我们的应用程序中要做的就是自定义一个Realm类,继承AuthorizingRealm抽象类,重载doGetAuthenticationInfo

(),重写获取用户信息的方法。

doGetAuthenticationInfo的重写

/**

* 认证信息.(身份验证) : Authentication 是用来验证用户身份

*

* @param token

* @return

* @throws AuthenticationException

*/

@Override

protected AuthenticationInfo doGetAuthenticationInfo(

AuthenticationToken authcToken) throws AuthenticationException {

System.out.println("身份认证方法:MyShiroRealm.doGetAuthenticationInfo()");

ShiroToken token = (ShiroToken) authcToken;

Map map = new HashMap();

map.put("nickname", token.getUsername());

map.put("pswd", token.getPswd());

SysUser user = null;

// 从数据库获取对应用户名密码的用户

List userList = sysUserService.selectByMap(map);

if(userList.size()!=0){

user = userList.get(0);

}

if (null == user) {

throw new AccountException("帐号或密码不正确!");

}else if(user.getStatus()==0){

/**

* 如果用户的status为禁用。那么就抛出DisabledAccountException

*/

throw new DisabledAccountException("帐号已经禁止登录!");

}else{

//更新登录时间 last login time

user.setLastLoginTime(new Date());

sysUserService.updateById(user);

}

return new SimpleAuthenticationInfo(user, user.getPswd(), getName());

}

通俗的说,这个的重写就是我们第一个学习目标的实现。

链接权限的实现

shiro的权限授权是通过继承AuthorizingRealm抽象类,重载doGetAuthorizationInfo();

当访问到页面的时候,链接配置了相应的权限或者shiro标签才会执行此方法否则不会执行,所以如果只是简单的身份认证没有权限的控制的话,那么这个方法可以不进行实现,直接返回null即可。

在这个方法中主要是使用类:SimpleAuthorizationInfo

进行角色的添加和权限的添加。

authorizationInfo.addRole(role.getRole());

authorizationInfo.addStringPermission(p.getPermission());

当然也可以添加set集合:roles是从数据库查询的当前用户的角色,stringPermissions是从数据库查询的当前用户对应的权限

authorizationInfo.setRoles(roles);

authorizationInfo.setStringPermissions(stringPermissions);

就是说如果在shiro配置文件中添加了filterChainDefinitionMap.put("/add", "perms[权限添加]");

就说明访问/add这个链接必须要有“权限添加”这个权限才可以访问,

如果在shiro配置文件中添加了filterChainDefinitionMap.put("/add", "roles[100002],perms[权限添加]");

就说明访问/add这个链接必须要有“权限添加”这个权限和具有“100002”这个角色才可以访问。

/**

* 授权

*/

@Override

protected AuthorizationInfo doGetAuthorizationInfo(

PrincipalCollection principals) {

System.out.println("权限认证方法:MyShiroRealm.doGetAuthenticationInfo()");

SysUser token = (SysUser)SecurityUtils.getSubject().getPrincipal();

String userId = token.getId();

SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

//根据用户ID查询角色(role),放入到Authorization里。

/*Map map = new HashMap();

map.put("user_id", userId);

List roleList = sysRoleService.selectByMap(map);

Set roleSet = new HashSet();

for(SysRole role : roleList){

roleSet.add(role.getType());

}*/

//实际开发,当前登录用户的角色和权限信息是从数据库来获取的,我这里写死是为了方便测试

Set roleSet = new HashSet();

roleSet.add("100002");

info.setRoles(roleSet);

//根据用户ID查询权限(permission),放入到Authorization里。

/*List permissionList = sysPermissionService.selectByMap(map);

Set permissionSet = new HashSet();

for(SysPermission Permission : permissionList){

permissionSet.add(Permission.getName());

}*/

Set permissionSet = new HashSet();

permissionSet.add("权限添加");

info.setStringPermissions(permissionSet);

return info;

}

这个类的实现是完成了我们学习目标的第二个任务。

编写web层的代码

登录页面:

controller

//跳转到登录表单页面

@RequestMapping(value="login")

public String login() {

return "login";

}

/**

* ajax登录请求

* @param username

* @param password

* @return

*/

@RequestMapping(value="ajaxLogin",method=RequestMethod.POST)

@ResponseBody

public Map submitLogin(String username, String password,Model model) {

Map resultMap = new LinkedHashMap();

try {

ShiroToken token = new ShiroToken(username, password);

SecurityUtils.getSubject().login(token);

resultMap.put("status", 200);

resultMap.put("message", "登录成功");

} catch (Exception e) {

resultMap.put("status", 500);

resultMap.put("message", e.getMessage());

}

return resultMap;

}

jsp

pageEncoding="utf-8"%>

String path = request.getContextPath();

String basePath = request.getScheme() + "://"

+ request.getServerName() + ":" + request.getServerPort()

+ path;

%>

登录

错误信息:

账号:

密码:

var username = $("#username").val();

var password = $("#password").val();

$("#ajaxLogin").click(function() {

$.post("/ajaxLogin", {

"username" : username,

"password" : password

}, function(result) {

if (result.status == 200) {

location.href = "/index";

} else {

$("#erro").html(result.message);

}

});

});

主页页面

controller

//跳转到主页

@RequestMapping(value="index")

public String index() {

return "index";

}

/**

* 退出

* @return

*/

@RequestMapping(value="logout",method =RequestMethod.GET)

@ResponseBody

public Map logout(){

Map resultMap = new LinkedHashMap();

try {

//退出

SecurityUtils.getSubject().logout();

} catch (Exception e) {

System.err.println(e.getMessage());

}

return resultMap;

}

jsp

pageEncoding="UTF-8"%>

String path = request.getContextPath();

String basePath = request.getScheme() + "://"

+ request.getServerName() + ":" + request.getServerPort()

+ path;

%>

Insert title here

helloJsp

$("#logout").click(function(){

location.href="/logout";

});

添加操作页面

controller

@RequestMapping(value="add")

public String add() {

return "add";

}

jsp

pageEncoding="UTF-8"%>

String path = request.getContextPath();

String basePath = request.getScheme() + "://"

+ request.getServerName() + ":" + request.getServerPort()

+ path;

%>

Insert title here

具有添加权限

测试

任务一

编写好后就可以启动程序,访问index页面,由于没有登录就会跳转到login页面。

登录之后就会跳转到index页面,点击退出登录后,有直接在浏览器中输入index页面访问,又会跳转到login页面

上面这些操作时候触发MyShiroRealm.doGetAuthenticationInfo()这个方法,也就是登录认证的方法。

任务二

登录之后访问add页面成功访问,在shiro配置文件中改变add的访问权限为

filterChainDefinitionMap.put("/add","perms[权限删除]");

再重新启动程序,登录后访问,会重定向到/403页面,由于没有编写403页面,报404错误。

上面这些操作,会触发权限认证方法:MyShiroRealm.doGetAuthorizationInfo(),每访问一次就会触发一次。

任务三

输入错误的用户名或则密码,返回“帐号或密码不正确!”的错误信息,在数据库中把一个用户的状态改为被禁用,再登陆,提示“帐号已经禁止登录!”的错误信息

上面的操作,是在MyShiroRealm.doGetAuthenticationInfo()登录认证的方法中实现的,通过查询数据库判断当前登录用户是否被禁用,具体可以去看源码。

总结

当然shiro很强大,这仅仅是完成了登录认证和权限管理这两个功能,接下来我会继续学习和分享,说说接下来的学习路线吧:

shiro+redis集成,避免每次访问有权限的链接都会去执行MyShiroRealm.doGetAuthenticationInfo()方法来查询当前用户的权限,因为实际情况中权限是不会经常变得,这样就可以使用redis进行权限的缓存。

实现shiro链接权限的动态加载,之前要添加一个链接的权限,要在shiro的配置文件中添加filterChainDefinitionMap.put("/add", "roles[100002],perms[权限添加]"),这样很不方便管理,一种方法是将链接的权限使用数据库进行加载,另一种是通过init配置文件的方式读取。

Shiro 自定义权限校验Filter定义,及功能实现。

Shiro Ajax请求权限不满足,拦截后解决方案。这里有一个前提,我们知道Ajax不能做页面redirect和forward跳转,所以Ajax请求假如没登录,那么这个请求给用户的感觉就是没有任何反应,而用户又不知道用户已经退出了。

Shiro JSP标签使用。

Shiro 登录后跳转到最后一个访问的页面

在线显示,在线用户管理(踢出登录)。

登录注册密码加密传输。

集成验证码。

记住我的功能。关闭浏览器后还是登录状态。

还有没有想到的后面再说,欢迎大家提出一些建议。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值