从一个正常的设计角度来讲,用户、角色、权限三者的关系数据两个多个多:
一个用户拥有多个角色,一个角色属于多个用户;
一个角色拥有多个权限,一个权限属于多个角色;
范例:数据库的创建脚本:
drop database if exists shirodb;
create database shirodb character set utf8;
use shirodb;
create table member (
mid varchar(50),
password varchar(32),
name varchar(20),
locked int,
constraint pk_mid primary key(mid)
);
insert into member(mid,password,name,locked) values('admin','hello','管理员',0);
insert into member(mid,password,name,locked) values('mldn','java','隔壁老王',0);
create table role (
rid int auto_increment,
title varchar(50),
flag varchar(50),
constraint pk_rid primary key (rid)
);
create table member_role (
mid varchar(50),
rid int
);
create table action (
actid int auto_increment,
title varchar(50),
flag varchar(50),
constraint pk_actid primary key (actid)
);
create table role_action (
rid int,
actid int
);
为了方便进行登录与授权处理,编写一个单独的程序类,这个类也不做业务层或数据层的划分了,只是做了简单的功能类,此类可以取得用户的信息以及角色和权限数据。
定义一个Member数据表的Vo类:
package com.gwolf.vo;
import java.io.Serializable;
public class Member implements Serializable{
private String mid;
private String password;
private String name;
public String getMid() {
return mid;
}
public void setMid(String mid) {
this.mid = mid;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
定义业务层的实现类:MemberLoginService
package com.gwolf.service;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;
import com.gwolf.vo.Member;
public class MemberLoginService {
private Connection connection;
private static final String DBDRIVER="org.gjt.mm.mysql.Driver";
private static final String DBURL="jdbc:mysql://localhost:3306/shirodb";
private static final String DBUSER = "root";
private static final String PASSWORD="root";
private PreparedStatement pstmt;
public MemberLoginService() {
this.connectionDataBase();
}
public Member get(String mid) {
Member vo = null;
try {
String sql = "select mid,password from member where mid=?";
this.pstmt = this.connection.prepareStatement(sql);
this.pstmt.setString(1, mid);
ResultSet rs = this.pstmt.executeQuery();
if(rs.next()) {
vo = new Member();
vo.setMid(rs.getString(1));
vo.setPassword(rs.getString(2));
}
} catch (SQLException e) {
e.printStackTrace();
}
return vo;
}
/**
* 根据用户名查询出永不对应的所有的角色数据
* @param mid
* @return
*/
public Set listRolesByMember(String mid) {
Set allRoles = new HashSet();
try {
String sql = "select flag from role where rid in"
+ "(select rid from member_role where mid=?)";
this.pstmt = this.connection.prepareStatement(sql);
this.pstmt.setString(1, mid);
ResultSet rs = this.pstmt.executeQuery();
while(rs.next()) {
allRoles.add(rs.getString(1));
}
} catch (SQLException e) {
e.printStackTrace();
}
return allRoles;
}
/**
* 根据用户名查询出所有的权限数据
* @param mid
* @return
*/
public Set listActionByMember(String mid) {
Set allAction = new HashSet();
try {
String sql = "select flag from action where actid in"
+ "(select actid from role_action where rid"
+ " in(select rid from member_role where mid=?))";
this.pstmt = this.connection.prepareStatement(sql);
this.pstmt.setString(1, mid);
ResultSet rs = this.pstmt.executeQuery();
while(rs.next()) {
allAction.add(rs.getString(1));
}
} catch (SQLException e) {
e.printStackTrace();
}
return allAction;
}
public void close() {
if(this.connection!=null) {
try {
this.connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
private void connectionDataBase() {
try {
Class.forName(DBDRIVER);
connection = DriverManager.getConnection(DBURL, DBUSER, PASSWORD);
} catch (Exception e) {
e.printStackTrace();
}
}
}
最终这个程序类所取得的全部的角色和权限数据都是需要交给Shiro判断的,在整个的处理过程之中,它的判断是利用Set集合完成的,所以必须返回Set集合。
public Set listRolesByMember(String mid) {
Set allRoles = new HashSet();
try {
String sql = "select flag from role where rid in"
+ "(select rid from member_role where mid=?)";
this.pstmt = this.connection.prepareStatement(sql);
this.pstmt.setString(1, mid);
ResultSet rs = this.pstmt.executeQuery();
while(rs.next()) {
allRoles.add(rs.getString(1));
}
} catch (SQLException e) {
e.printStackTrace();
}
return allRoles;
}
此时的操作不在是简单的用户登录了,但是现在除了用户登录之外还牵扯到权限的操作,所以整个的自定的Realm类就必须更换一个父类:
认证处理类:AuthenticatingRealm。
授权处理类:AuthorizingRealm。
自定义认证授权Realm:
package com.gwolf.shiro.realm;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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 com.gwolf.service.MemberLoginService;
import com.gwolf.vo.Member;
public class MyRealm extends AuthorizingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//登录认证的方法需要先执行,需要用他来判断登录的用户信息是否合法
String username = (String)token.getPrincipal();
MemberLoginService memberLoginService = new MemberLoginService();
Member vo = memberLoginService.get(username);
memberLoginService.close();
if(vo == null) {
throw new AuthenticationException("改用户名称不存在");
}else {
String password = new String((char[])token.getCredentials());
if(vo.getPassword().equals(password)) {
AuthenticationInfo auth = new SimpleAuthenticationInfo(username, password,"memberRealm");
return auth;
}else {
throw new IncorrectCredentialsException("密码错误!");
}
}
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String)principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authenticationInfo = new SimpleAuthorizationInfo();
MemberLoginService memberLoginService = new MemberLoginService();
authenticationInfo.setRoles(memberLoginService.listRolesByMember(username));
authenticationInfo.setStringPermissions(memberLoginService.listActionByMember(username));
memberLoginService.close();
return authenticationInfo;
}
}
需要在shiro.ini文件里面定义相关的Realm配置:
[main]
jdbcRealm=com.gwolf.shiro.realm.MyRealm
securityManager.realm=$jdbcRealm
在单元测试类中测试程序是否正确执行:
package com.gwolf.shiro;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
public class TestLoginDemo {
public static void main(String[] args) {
//取得Factory接口对象,主要的目的是通过配置文件加载文之中的信息,这些信息暂时不能成为认证信息
//取得里面所保存的所有的认证数据信息
SecurityManager securityManager = new IniSecurityManagerFactory("classpath:shiro.ini").getInstance();
//利用一个专门的认证操作的处理类,实现认证处理的具体实现
SecurityUtils.setSecurityManager(securityManager);
//获取进行用户名和密码认证的接口对象
Subject subject = SecurityUtils.getSubject();
subject.login(new UsernamePasswordToken("admin", "hello"));
System.out.println(subject.isPermitted("member:add"));
}
}
领取专属 10元无门槛券
私享最新 技术干货