代码实现
login.jsp
<%@page contentType="text/html; UTF-8" pageEncoding="utf-8" isELIgnored="false" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>用户登录</h1>
<form action="${pageContext.request.contextPath}/user/login" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="text" name="password"> <br>
<input type="submit" value="登录">
</form>
</body>
</html>
index.jsp:
<%@page contentType="text/html; UTF-8" pageEncoding="utf-8" isELIgnored="false" %>
<%--引用shiro的标签--%>
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>系统用户</h1>
<%--拿到用户名的标签--%>
<h1><shiro:principal/></h1>
<shiro:authenticated>
认证之后才能展示的内容,如果没有认证就不会展示,可以用于公共页面。
</shiro:authenticated>
<shiro:notAuthenticated>
没有认证之后的展示内容
</shiro:notAuthenticated>
<ul>
<%-- 设置多个用户--%>
<shiro:hasAnyRoles name="admin,user">
<li><a href="href">用户管理</a>
<ul>
<shiro:hasPermission name="user:add:*">
<li><a href="">添加</a></li>
</shiro:hasPermission>
<shiro:hasPermission name="user:delete:*">
<li><a href="">删除</a></li>
</shiro:hasPermission>
<shiro:hasPermission name="user:update:*">
<li><a href="">修改</a></li>
</shiro:hasPermission>
<shiro:hasPermission name="order:find:*">
<li><a href="">查询</a></li>
</shiro:hasPermission>
</ul>
</li>
</shiro:hasAnyRoles>
<%-- shiro标签的功能,在这个标签里面的链接能被admian使用,其他角色是看不见的--%>
<shiro:hasRole name="admin">
<li><a href="href">商品管理</a></li>
</shiro:hasRole>
<a href="${pageContext.request.contextPath}/user/logout">退出登录</a>
</ul>
</body>
</html>
userController:
/**
* 用来处理身份认证
* @param username
* @param password
* @return
*/
@RequestMapping("login")
public String login(String username,String password,String code,HttpSession session){
// 比较验证码
String codes = (String) session.getAttribute("code");
try {
if (codes.equalsIgnoreCase(code)) {
// 获取主体对象,和Java项目相比,是不用另外去创建安全管理器的,因为我们在shiro的配置类中声明了安全管理器。
Subject subject = SecurityUtils.getSubject();
subject.login(new UsernamePasswordToken(username, password));
return "redirect:/index.jsp";
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("用户名错误");
} catch (IncorrectCredentialsException e) {
e.printStackTrace();
System.out.println("密码错误");
}catch (Exception e){
e.printStackTrace();
System.out.println(e.getMessage());
}
return "redirect:/login.jsp";
}
customerRealm:
package shiro.demo.realms;
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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.util.CollectionUtils;
import shiro.demo.Untils.ApplicationContextUtils;
import shiro.demo.Untils.MyByteSource;
import shiro.demo.entity.Pers;
import shiro.demo.entity.Role;
import shiro.demo.entity.User;
import shiro.demo.service.UserService;
import java.util.List;
//自定义realm
public class CustomerRealm extends AuthorizingRealm {
@Override
// realm授权,用这个方法去获取权限信息
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 获取身份信息
String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
// 根据主身份信息获取角色和权限信息(测试数据)
// if("xiaochen".equals(primaryPrincipal)){
// SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
// /**
// * //给这个对象添加普通用户的角色,但是问题是,就算是添加了用户角色,
// * 但是系统并不知道普通用户具有什么样的权限,获取超级管理员又有什么权限,
// * 所以要在前端页面进行加上一些标签进行权限控制,就规定普通用户能看到那些东西
// */
// simpleAuthorizationInfo.addRole("user");
// simpleAuthorizationInfo.addStringPermission("user:update:*");
// simpleAuthorizationInfo.addStringPermission("user:select:*");
// return simpleAuthorizationInfo;
// }
UserService userServiceImpl = (UserService) ApplicationContextUtils.getBean("userServiceImpl");
User user = userServiceImpl.findRolesByUserName(primaryPrincipal);
// 授权角色信息
if (!CollectionUtils.isEmpty(user.getRoles())){
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
for (Role role : user.getRoles()) {
System.out.println(role.getRoleId());
// 角色信息
simpleAuthorizationInfo.addRole(role.getRoleName());
// 权限信息
List<Pers> pers = userServiceImpl.findPersByRoleId(role.getRoleId());
System.out.println(pers);
if (!CollectionUtils.isEmpty(pers)){
for (Pers per : pers) {
simpleAuthorizationInfo.addStringPermission(per.getName());
}
}
}
return simpleAuthorizationInfo;
}
// 连接数据库进行授权的代码块
// 通过工厂去拿业务对象
return null;
}
//realm认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 获取身份信息
String principal = (String) authenticationToken.getPrincipal();
// 在工厂中获取service对象,并且强转类型,获取后台返回的user对象
UserService userServiceImpl = (UserService) ApplicationContextUtils.getBean("userServiceImpl");
User user = userServiceImpl.findByUserName(principal);
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal, "123", this.getName());
if(user!=null){
return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(), new MyByteSource(user.getSalt()),this.getName());
}
return null;
}
}
ApplicationContextUtils:这个类的主要目的是,我们在serviceimpl中获取到返回的user对象,但是我们的realm拿不到这个对象,因为realm没有在工厂中, 没有办法获取注入的值,所以这个工具类的作用就是,帮助realm获取工厂注入的对象
package shiro.demo.Untils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* 这个类的主要目的是,我们在serviceimpl中获取到返回的user对象,但是我们的realm拿不到这个对象,因为realm没有在工厂种
* 没有办法获取注入的值,所以这个工具类的作用就是,帮助realm获取工厂注入的对象。
*/
@Component
public class ApplicationContextUtils implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context=applicationContext;
}
// 根据bean的名字来获取指定的bean对象
public static Object getBean(String beanName){
Object bean = context.getBean(beanName);
return bean;
}
}
userServiceImpl:
三个方法,
一个是根据用户名去对比数据库中的数据看是否可以登录成功。
一个是根据用户名去获取这个用户的角色
另一个是根据角色名去获取权限
@Override
public User findByUserName(String username) {
return userDao.findByUserName(username);
}
@Override
public User findByUserName(String username) {
return userDao.findByUserName(username);
}
@Override
public User findRolesByUserName(String username) {
return userDao.findRolesByUserName(username);
}
userDao:
User findByUserName(String username);
User findRolesByUserName(String username);
// 根据角色的id查询权限集合
List<Pers> findPersByRoleId(Integer roleId);
userDao.xml:
<select id="findByUserName" resultType="shiro.demo.entity.User">
select id,username,password,salt from t_user
where username=#{username}
</select>
<resultMap id="userMap" type="User">
<id column="id" property="id"/>
<result column="username" property="username"/>
<!-- 角色信息-->
<collection property="roles" javaType="list" ofType="Role">
<id column="role_id" property="roleId"/>
<result column="role_name" property="roleName"/>
</collection>
</resultMap>
<select id="findRolesByUserName" parameterType="String" resultMap="userMap">
select u.id,u.username,r.role_id,r.role_name from t_user u
LEFT JOIN t_user_role ur on u.id=ur.user_id
LEFT JOIN t_role r on ur.role_id=r.role_id
where u.username=#{username}
</select>
<select id="findPersByRoleId" resultType="shiro.demo.entity.Pers">
select p.* from t_role r
LEFT JOIN t_role_pers rp
ON r.role_id=rp.role_id
LEFT JOIN t_pers p
ON p.id=rp.pers_id
where r.role_id=#{roleId}
</select>
shiroConfig:
在config中设置紫铜的受限资源和公共资源
package shiro.demo.config;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import shiro.demo.Cache.RedisCacheManager;
import shiro.demo.realms.CustomerRealm;
import java.util.HashMap;
import java.util.Map;
//这是用来整合shiro框架相关的配置类
@Configuration
public class shiroConfig {
// 1,创建shiroFilter,负责拦截所有请求的
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 给filter设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
// 配置系统的受限资源
// 配置系统的公共资源
Map<String, String> map = new HashMap<>();
map.put("/user/register","anon");//设置注册方式为公共资源
map.put("/register.jsp","anon");//设置注册页面为公共资源
map.put("/user/login","anon"); //anon 就是不用认证和授权,是公共的资源
map.put("/user/getImg","anon");
map.put("/**","authc");//authc 请求这个资源需要认证和授权,除了上面的login.jsp资源不受限,其他的资源都是受限的。
System.out.println(map);
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
//默认认证的界面路径,这个地方不写默认的就是login.jsp页面,但是也可以改成别的。
// shiroFilterFactoryBean.setLoginUrl("/login.jsp");
return shiroFilterFactoryBean;
}
// 2,创建安全管理器
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("getRealm") Realm realm){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
// 给安全管理器设置realm
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
// 3,创建自定义realm
@Bean
public Realm getRealm(){
CustomerRealm customerRealm = new CustomerRealm();
// 因为这里我们使用MD5+salt+hash的密码进行匹配的,所以我们要更改一下默认的密码匹配器
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// 设置加密算法为MD5
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
// 设置散列次数
hashedCredentialsMatcher.setHashIterations(1024);
customerRealm.setCredentialsMatcher(hashedCredentialsMatcher);
}