1、mysql - 5.7 2、navicat(mysql客户端管理工具) 3、idea 2017.2 4、jdk8 5、tomcat 8.5 6、springboot2.1.3 7、mybatis 3 8、shiro1.4 9、maven3.3.9
添加 web、lombok、thymeleaf、jdbc、mysql、mybatis等模块;
SysUser.java
package com.qf.domain; import lombok.Data; import java.io.Serializable; import java.util.Date; /** * Created by 54110 on 2019-07-05. */ @Data public class SysUser implements Serializable { private int userId;//用户id private String loginName;//登录名 private String password;// private Integer state; private Date createTime; private String realname; }SysPermission.java
package com.qf.domain; import lombok.Data; import java.io.Serializable; /** * Created by 54110 on 2019-07-05. */ @Data public class SysPermission implements Serializable { private int permId; private String permName;//权限名称 private String permUrl;//权限操作地址(路径) private String menuName;//菜单名 private String menuLevel;//菜单级别(11:一级;12:二级。。。) private String menuCode;//菜单编码(每级两位数字) private int ifValid; private String parentCode; }SysUserMapper.java
package com.qf.mapper; import com.qf.domain.SysUser; import org.apache.ibatis.annotations.Mapper; /** * Created by 54110 on 2019-07-05. */ @Mapper public interface SysUserMapper { public SysUser findUserByUsername(String username); }SysPermissionMapper
package com.qf.mapper; import com.qf.domain.SysPermission; import org.apache.ibatis.annotations.Mapper; import java.util.List; /** * Created by 54110 on 2019-07-05. */ @Mapper public interface SysPermissionMapper { // 根据用户登录名查询其所拥有的权限 public List<SysPermission> findPermissionsByLoginName(String loginName); }SysUsersMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.qf.mapper.SysUserMapper"> <resultMap type="com.qf.domain.SysUser" id="userMap"> <id column="USERID" property="userid" /> <result column="LOGIN_NAME" property="loginName" /> <result column="PASSWORD" property="password" /> <result column="STATE" property="state" /> <result column="CREATE_TIME" property="createTime" /> <result column="REALNAME" property="realname" /> </resultMap> <sql id="tbusers_columns"> PASSWORD,LOGIN_NAME,CREATE_TIME,REALNAME,STATE </sql> <!--根据用户名查询对象 --> <select id="findUserByUsername" parameterType="string" resultMap="userMap"> SELECT <include refid="tbusers_columns"></include> FROM TB_SYS_USER US WHERE US.LOGIN_NAME = #{name} </select> </mapper>SysPermissionMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.qf.mapper.SysPermissionMapper"> <resultMap type="com.qf.domain.SysPermission" id="permMap"> <id column="PERMISSION_ID" property="permId" /> <result column="PER_NAME" property="permName" /> <result column="MENU_URL" property="permUrl" /> <result column="MENU_NAME" property="menuName" /> <result column="MENU_TYPE" property="menuLevel" /> <result column="MENU_CODE" property="menuCode" /> <result column="PARENT_CODE" property="parentCode" /> <result column="IF_ViLID" property="ifValid" /> </resultMap> <select id="findPermissionsByLoginName" parameterType="string" resultMap="permMap"> SELECT p.* FROM TB_SYS_USER us , TB_USER_ROLE ur, TB_SYS_ROLE r, TB_ROLE_PERMISSION rp, TB_SYS_PERMISSION p WHERE us.USERID = ur.USER_ID AND ur.ROLE_ID = r.ROLE_ID AND r.ROLE_ID = rp.ROLE_ID AND rp.PERMISSION_ID = p.PERMISSION_ID AND trim(us.LOGIN_NAME) = #{loginName} ORDER BY p.MENU_CODE </select> </mapper>SysUsersServiceImpl.java
package com.qf.service.impl; import com.qf.domain.SysUser; import com.qf.mapper.SysUserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * Created by 54110 on 2019-07-05. */ @Service public class SysUsersServiceImpl { @Autowired private SysUserMapper userMapper; public SysUser queryUserByLoginName(String loginName) { SysUser tbUsers = userMapper.findUserByUsername(loginName); return tbUsers; } }
SysPermissionServiceImpl.java
package com.qf.service.impl; import com.qf.domain.SysPermission; import com.qf.mapper.SysPermissionMapper; import com.qf.service.SysPermissionService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; /** * Created by 54110 on 2019-07-05. */ @Service public class SysPermissionServiceImpl implements SysPermissionService { @Autowired private SysPermissionMapper permMapper; @Override public List<SysPermission> queryPermissionsByLoginName(String loginName) { List<SysPermission> list = permMapper.findPermissionsByLoginName(loginName); return list; } }UserController.java
package com.qf.controller; import com.qf.service.SysUserService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import java.util.Map; /** * Created by 54110 on 2019-07-05. */ @Controller public class UserController { @Autowired private SysUserService userService; // 登录页(view)展示 @RequestMapping("/login") public String showlogin(){ return "login"; } /** * 登录处理 * @param map 用户登录表单数据 * @return 逻辑视图 */ @RequestMapping(value="dealLogin" ,method= RequestMethod.POST) public String dealLogin(@RequestParam Map<String,Object> map){ System.out.println( map.values().toString()); try { Subject subject = SecurityUtils.getSubject();//从安全管理器中获取主体对象 UsernamePasswordToken token = new UsernamePasswordToken();//构建令牌对象 token.setUsername(map.get("name").toString());//赋身份信息 token.setPassword(map.get("password").toString().toCharArray());//赋凭证信息 subject.login(token);//使用主体的login方法判定用户的权限 if(subject.isAuthenticated()){ // 已登陆 // 用户信息及权限信息的存储(session|| redis) return "main"; } } catch (AuthenticationException e) { e.printStackTrace(); System.out.println("登录失败"); } return "login"; } // 登录且拥有user: @RequestMapping("/one") public String showCaseOne(){ return "one"; } @RequestMapping("/two") public String showCaseTwo(){ return "two"; } // 权限不足时,响应的页面 @RequestMapping("/unauth") public String showPermission(){ return "unauth"; } // 用户注销操作 @RequestMapping("/logout") public String logout(){ Subject subject = SecurityUtils.getSubject(); subject.logout();//登出 return "redirect:login"; } }a、自定义安全策略
MyShiroRealm.java
package com.qf.shiro; import com.qf.domain.SysPermission; import com.qf.domain.SysUser; import com.qf.service.SysPermissionService; import com.qf.service.SysUserService; import org.apache.shiro.SecurityUtils; 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.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import java.util.Collection; import java.util.HashSet; import java.util.List; /** * Created by 54110 on 2019-07-05. */ public class MyShiroRealm extends AuthorizingRealm { @Autowired private SysUserService sysUserServiceImpl; @Autowired private SysPermissionService sysPermissionServiceImpl; private String username; // 系统授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { Subject subject = SecurityUtils.getSubject();//获取主体对象 String username =(String ) subject.getPrincipal();//获取用户身份信息 List<SysPermission> permissions = sysPermissionService.queryPermissionsByLoginName(username);//根据用户名获取用户的权限信息 // 权限去重 Collection<String > perms = new HashSet<>(); for (SysPermission perm: permissions ) { perms.add(perm.getPermName()); } SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); simpleAuthorizationInfo.addStringPermissions(perms);//授权 return simpleAuthorizationInfo; } //用户认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal();//获取用户信息 // char[] credentials =(char[]) token.getCredentials(); // String password = ((char[]) token.getCredentials()).toString();//获取凭证信息 //根据用户信息查询数据库获取后端的用户身份,转交给securityManager判定 SysUser user1 = sysUserService.queryUserByLoginName(username);//从数据库直接取 System.out.println(user1); if(user1!=null) { SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user1.getLoginName(), user1.getPassword(), getName()); return simpleAuthenticationInfo; } return null; } }b、自定义Shiro配置管理
ShiroConfig.java
package com.qf.config; import com.qf.shiro.MyShiroRealm; 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 java.util.HashMap; import java.util.Map; /** * Created by 54110 on 2019-07-05. */ @Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager); Map<String ,String> map = new HashMap<>(); map.put("/main","authc"); //必须登录才可访问 map.put("/one","perms[user_edit]");//只有特定权限(“user_edit”)的用户登录后才可访问 map.put("/two","perms[user_forbidden]");//只有特定权限(“user_forbidden”)的用户登录后才可访问 shiroFilterFactoryBean.setLoginUrl("/login");//设置登录页(匿名) shiroFilterFactoryBean.setUnauthorizedUrl("/unauth");//权限不足的错误提示页 shiroFilterFactoryBean.setFilterChainDefinitionMap(map);//装配拦截策略 return shiroFilterFactoryBean; } // 配置安全管理器(注入Realm对象) @Bean(name="defaultWebSecurityManager") public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("myShiroRealm") MyShiroRealm myShiroRealm){ DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(); defaultWebSecurityManager.setRealm(myShiroRealm); return defaultWebSecurityManager; } @Bean(name="myShiroRealm") //使用该注解是的Realm对象由spring容器管理 public MyShiroRealm myShiroRealm(){ MyShiroRealm shiroRealm = new MyShiroRealm(); return shiroRealm; } } 地址栏输入http://localhost:8080/login
使用用户:admin2密码:12345登录,登录成功后显示页面如下:
因为admin2用户拥有case one功能的操作权限,所以当鼠标单击case one链接时,显示如下成功访问页面
因为admin2没有case two访问权限,当用户单击case two时,会显示无权限访问的页面:
当单击logout链接时,系统重回登录页。此时使用用户test2密码test2再次登录。因test2用户无case one权限,有case two权限,所以当test2用户单击case two时会显示如下页面:
此事有权限访问的也页面正常,但未授权的页面,无法进入提示页,显示如下:
后台亦抛出org.apache.shiro.authz.AuthorizationException异常:
在数据表中存的密码不应该是12345,而应该是12345加密之后的字符串,而且还要求这个加密算法是不可逆的,即由加密后的字符串不能反推回来原来的密码,如果能反推回来那这个加密是没有意义的。
著名的加密算法,比如 MD5,SHA1
1). 如何把一个字符串加密为MD5
2). 使用MD5加密算法后,前台用户输入的字符串如何使用MD5加密,需要做的是将当前的Realm 的credentialsMatcher属性,替换为Md5CredentialsMatcher 由于Md5CredentialsMatcher已经过期了,推荐使用HashedCredentialsMatcher 并设置加密算法即可。
1). 修改ShiroConfig.java文件添加如下内容;
/** * 密码校验规则HashedCredentialsMatcher * 这个类是为了对密码进行编码的 , * 防止密码在数据库里明码保存 , 当然在登陆认证的时候 , * 这个类也负责对form里输入的密码进行编码 * 处理认证匹配处理器:如果自定义需要实现继承HashedCredentialsMatcher */ @Bean("hashedCredentialsMatcher") public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); //指定加密方式为MD5 credentialsMatcher.setHashAlgorithmName("MD5"); //加密次数 credentialsMatcher.setHashIterations(1024); credentialsMatcher.setStoredCredentialsHexEncoded(true); return credentialsMatcher; } @Bean("myShiroRealm") public MyShiroRealm myShiroRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher) { MyShiroRealm authRealm = new MyShiroRealm(); authRealm.setAuthorizationCachingEnabled(false); authRealm.setCredentialsMatcher(matcher); return authRealm; } 2).修改MyRealm.java的认证逻辑如下:
//用户认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal();//获取用户信息 // char[] credentials =(char[]) token.getCredentials(); // String password = ((char[]) token.getCredentials()).toString();//获取凭证信息 //根据用户信息查询数据库获取后端的用户身份,转交给securityManager判定 SysUser user1 = sysUserService.queryUserByLoginName(username);//从数据库直接取 System.out.println(user1); if(user1!=null) { //当前realm对象的name String realmName = getName(); //盐值 ByteSource credentialsSalt = ByteSource.Util.bytes(username); //封装用户信息,构建AuthenticationInfo对象并返回 AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(username, user1.getPassword(), credentialsSalt, realmName); return authcInfo; // SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user1.getLoginName(), user1.getPassword(), getName()); // return simpleAuthenticationInfo; } return null; } 3). 通过 new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations); 我们可以得到"12345"经过MD5 加密1024后的字符串;
package com.jeffrey; import org.apache.shiro.crypto.hash.SimpleHash; import org.apache.shiro.util.ByteSource; /** * Created by jeffrey on 2019/4/8. */ public class MD5Salt { public static void main(String[] args){ String hashAlgorithName = "MD5";//加密算法 String password = "12345";//登陆时的密码 int hashIterations =1024;//加密次数 ByteSource credentialsSalt = ByteSource.Util.bytes("admin2");//使用登录名做为salt SimpleHash simpleHash = new SimpleHash(hashAlgorithName, password, credentialsSalt, hashIterations); System.out.println("ok "+simpleHash); } } 使用密文替换数据库中的明文密码;