Apache Shiro是一个强大而灵活的开源安全框架,它能够干净利落地处理身份认证,授权,企业会话管理和加密(核心功能)。
以下是你可以用 Apache Shiro所做的事情:
1. 验证用户
2.对用户执行访问控制,如:
判断用户是否拥有角色admin。
判断用户是否拥有访问的权限
3.在任何环境下使用 Session API。例如CS程序。
4.可以使用多个用户数据源。例如一个是oracle用户库,另外一个是mysql用户库。
5. 单点登录(SSO)功能。
6.“Remember Me”服务 ,类似购物车的功能,shiro官方建议开启
Application Code:应用程序代码,由开发人员负责开发
subject:主体,当前用户
security manager:安全管理器,框架的核心,负责调用realm,框架提供
realm:类似于dao,访问安全数据(权限、角色、用户),框架提供,也可以自定义
第一步:导包
第二步:在web.xml里面配置由spring框架提供整合shero的过滤器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<!-- 指定spring配置文件 -->
<
context-param
>
<
param-name
>contextConfigLocation</
param-name
>
<
param-value
>classpath:applicationContext.xml</
param-value
>
</
context-param
>
<
listener
>
<
listener-class
>org.springframework.web.context.ContextLoaderListener</
listener-class
>
</
listener
>
<!-- 配置权限管理 -->
<
filter
>
<
filter-name
>shiroFilter</
filter-name
>
<
filter-class
>org.springframework.web.filter.DelegatingFilterProxy</
filter-class
>
</
filter
>
<
filter-mapping
>
<
filter-name
>shiroFilter</
filter-name
>
<
url-pattern
>/*</
url-pattern
>
</
filter-mapping
>
|
第三步:在applicationContext.xml里面配置一个bean,并且id要和过滤器的名字一样
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<!-- 第一种代码配置设置shiro权限 -->
<
bean
id
=
"shiroFilter"
class
=
"org.apache.shiro.spring.web.ShiroFilterFactoryBean"
>
<
property
name
=
"securityManager"
ref
=
"securityManager"
></
property
>
<
property
name
=
"loginUrl"
value
=
"/login.jsp"
></
property
>
<
property
name
=
"successUrl"
value
=
"/index.jsp"
></
property
>
<
property
name
=
"unauthorizedUrl"
value
=
"/500.jsp"
></
property
>
<
property
name
=
"filterChainDefinitions"
>
<
value
>
/css/**=anon
/js/**=anon
/images/**=anon
/login.jsp*=anon
/validatecode.jsp*=anon
/UserAction_login.action*=anon
/page_base_staff.action=perms["staff"]
/**=authc
</
value
>
</
property
>
</
bean
>
|
第四步:在applicationConte.xml里面配置安全管理器,并且注入Realm
1
2
3
4
5
|
<
bean
id
=
"securityManager"
class
=
"org.apache.shiro.web.mgt.DefaultWebSecurityManager"
>
<
property
name
=
"realms"
ref
=
"shiroRealms"
></
property
>
<
property
name
=
"cacheManager"
ref
=
"cache"
></
property
>
</
bean
>
<
bean
id
=
"shiroRealms"
class
=
"com.mickeymouse.bos.shiro.ShiroRealms"
></
bean
>
|
第五步:编写一个realm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
package
com.mickeymouse.bos.shiro;
import
java.util.List;
import
javax.annotation.Resource;
import
org.apache.shiro.authc.AuthenticationException;
import
org.apache.shiro.authc.AuthenticationInfo;
import
org.apache.shiro.authc.AuthenticationToken;
import
org.apache.shiro.authc.SimpleAuthenticationInfo;
import
org.apache.shiro.authc.UsernamePasswordToken;
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.springframework.beans.factory.annotation.Autowired;
import
com.mickeymouse.bos.dao.IFunctionDao;
import
com.mickeymouse.bos.dao.IUserDao;
import
com.mickeymouse.bos.domain.Function;
import
com.mickeymouse.bos.domain.User;
public
class
ShiroRealms
extends
AuthorizingRealm{
@Autowired
private
IUserDao iUserDao;
@Resource
private
IFunctionDao iFunctionDao;
/**
* 授权
*/
@Override
protected
AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
//获取认证信息
SimpleAuthorizationInfo authorizationInfo =
new
SimpleAuthorizationInfo();
//通过认证信息获取当前用户
User user = (User) principals.getPrimaryPrincipal();
//先判断是不是超管
if
(user.getUsername()==
"admin"
) {
//超管:直接查询所有权限
List<Function> list = iFunctionDao.findAll();
for
(Function
function
: list) {
authorizationInfo.addStringPermission(
function
.getCode());
}
}
else
{
//普通用户:通过用户ID查询所具有的权限
List<Function> list = iFunctionDao.findFunctionByUserid(user.getId());
for
(Function
function
: list) {
authorizationInfo.addStringPermission(
function
.getCode());
}
}
return
authorizationInfo;
}
/**
* 认证:判断用户名与密码是否正确
*/
@Override
protected
AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
//通过认证标记获取传入的用户名
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
String
username = usernamePasswordToken.getUsername();
//通过用户名查询数据库返回对象
User user = iUserDao.findByUserFronUsername(username);
if
(user==
null
) {
//如果为空直接返回null,此时subject会直接抛出异常
return
null
;
}
else
{
//账号存在,将从数据库中查询出的密码交给安全管理器比对
//三个参数:第一个验证正确后需要的返回值,第二个:安全管理器需要的比对密码,第三个:realm的类名
//简单认证信息对象
AuthenticationInfo authenticationInfo =
new
SimpleAuthenticationInfo(user,user.getPassword(),
this
.getName());
return
authenticationInfo;
}
}
}
|
第六步:使用shero框架完成登录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
/**
* 登录
* @return
*/
public
String
login(){
String
key = (
String
) ActionContext.getContext().getSession().
get
(
"key"
);
//取出用户名密码为空
if
(!StringUtils.isEmpty(t.getUsername())&&!StringUtils.isEmpty(t.getPassword())) {
//去除验证码错误或空
if
(!StringUtils.isEmpty(checkcode)&&!StringUtils.isEmpty(key)&&key.equals(checkcode)) {
//获取当前用户,此时的状态为"未认证"
Subject subject = SecurityUtils.getSubject();
//创建用户名和密码的认证标记
AuthenticationToken authenticationToken =
new
UsernamePasswordToken(t.getUsername(), MD5Utils.md5(t.getPassword()));
try
{
//调用登录方法,传入认证标记
subject.login(authenticationToken);
//如果没有异常,那么返回"认证信息传出的返回值对象"
User user = (User) subject.getPrincipal();
//存入session域中
ActionContext.getContext().getSession().put(
"User"
,user);
return
"home"
;
}
catch
(Exception e) {
e.printStackTrace();
this
.addActionMessage(
"用户名或者密码不正确~"
);
}
}
else
{
this
.addActionMessage(
"验证码错误!"
);
}
}
else
{
this
.addActionMessage(
"用户名或者密码不能为空!"
);
}
return
"login"
;
}
|
异常信息:
当用户名错误,认证信息为空抛出异常
当密码错误,安全管理器抛出异常
第一步:导包
第二步:在applicationContext.xml里开启shero注解
1
2
3
4
5
6
|
<!-- 第二种:注解方式设置shiro权限 -->
<!--
1
. 开启shiro注解 -->
<bean id=
"defaultAdvisorAutoProxyCreator"
class
=
"org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
>
<property name=
"proxyTargetClass"
value=
"true"
></property>
</bean>
<bean
class
=
"org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"
></bean>
|
第三步:在Action的方法上使用shiro注解描述需要具有的权限
1
2
3
4
5
6
7
8
9
10
11
|
/**
* 分页查询
*/
@RequiresPermissions
(value=
"region"
)
//执行这个方法需要具有region.query这个权限
@RequiresRoles
(value=
"admin"
)
public
String pageQuery(){
regionService.pageQuery(pageBean);
String[] excludes =
new
String[]{
"subareas"
};
this
.writePageBean2Json(pageBean, excludes );
return
NONE;
}
|
第四步:注意修改action抽取类的构造方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public
ActionBase(){
ParameterizedType parameterizedType =
null
;
Type genericSuperclass =
this
.getClass().getGenericSuperclass();
if
(genericSuperclass
instanceof
ParameterizedType) {
parameterizedType = (ParameterizedType)
this
.getClass().getGenericSuperclass();
}
else
{
//这里的this为shero的代理对象
parameterizedType = (ParameterizedType)
this
.getClass().getSuperclass().getGenericSuperclass();
}
//获得BaseAction类定义的泛型数组
Type[] types = parameterizedType.getActualTypeArguments();
Class<T> class1 = (Class<T>) types[
0
];
try
{
t = class1.newInstance();
}
catch
(InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch
(IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
|