Shrio认证与盐加密

Shrio认证流程
主体(subject)需要携带身份信息和凭证信息,shiro在认证时会将这些信息打包成一个令牌,进入到安全管理器中进行认证。

 在 shiro 中,用户需要提供principals (身份)和credentials(凭证)给shiro,从而应用能验证用户身份。身份即帐号/凭证即密码
 

Shrio登录认证案例 SSM

1.导入Shrio相关依赖

  <!--  shiro核心包  -->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-core</artifactId>
      <version>${shiro.version}</version>
    </dependency>
    <!--  添加shiro web支持  -->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-web</artifactId>
      <version>${shiro.version}</version>
    </dependency>
    <!--  添加shiro spring支持  -->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>${shiro.version}</version>
    </dependency>

2.配置Shrio过滤器(web.xml)

<!--  shiro过滤器定义  -->
  <filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
      <!--  该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理  -->
      <param-name>targetFilterLifecycle</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

3.通过逆向工程将五张表生成对应的 model、mapper

4.在SysUserMapper中加入登录的方法和对应的sql

 /**
     * 根据账号查询
     * @param username
     * @return
     */
    SysUser userLogin(@Param("username") String username);
 
----------------------------------------------------------------------------
  <select id="userLogin" resultType="com.zking.ssm.book.model.SysUser">
    select <include refid="Base_Column_List"/> from  t_sys_user
    where username=#{username}
  </select>

5.自定义数据源ShrioRealm

/**
 * 自定义Realm的安全数据源,采用数据库的方式
 */
public class ShiroRealm extends AuthorizingRealm {
 
    @Autowired
    private SysUserMapper sysUserMapper;
 
 
    /**
     *授权
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        
        return null;
    }
 
    /**
     * 认证
     * @param authenticationToken 传入的账号密码令牌Token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //获取账号
        String username = authenticationToken.getPrincipal().toString();
        //获取密码
        String password = authenticationToken.getCredentials().toString();
 
        //根据账号查询数据库中的用户对象信息
        SysUser sysUser = sysUserMapper.userLogin(username);
        //判断账号是否存在
        if(sysUser ==null){
            throw new UnknownAccountException("账号不存在!!!");
        }
 
        //创建SimpleAuthenticationInfo,传入正确的账号和密码(来自于数据库)
        SimpleAuthenticationInfo simple=new SimpleAuthenticationInfo(
                sysUser.getUsername(),
                sysUser.getPassword(),
                this.getName()
        );
 
        return simple;
    }
}

6.配置数据源的管理文件spring-shrio.xml并加入spring.xml中

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 
    <!--1、注册自定义的Realm 采用数据库方式-->
    <bean id="shiroRealm" class="com.zking.ssm.book.shiro.ShiroRealm">
        <!--配置Shiro明文密码如何进行加密-->
        <!--注意:重要的事情说三次~~~~~~此处加密方式要与用户注册时的算法一致 -->
        <!--注意:重要的事情说三次~~~~~~此处加密方式要与用户注册时的算法一致 -->
        <!--注意:重要的事情说三次~~~~~~此处加密方式要与用户注册时的算法一致 -->
        <!--以下三个配置告诉shiro将如何对用户传来的明文密码进行加密-->
        <property name="credentialsMatcher">
            <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <!--指定hash算法为MD5-->
                <property name="hashAlgorithmName" value="md5"/>
                <!--指定散列次数为1024次-->
                <property name="hashIterations" value="1024"/>
                <!--true指定Hash散列值使用Hex加密存. false表明hash散列值用用Base64-encoded存储-->
                <property name="storedCredentialsHexEncoded" value="true"/>
            </bean>
        </property>
    </bean>
 
    <!--2、创建安全管理器,并更换Realm-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="shiroRealm" />
        <property name="sessionManager" ref="sessionManager"/>
        <!--设置缓存管理器-->
        <property name="cacheManager" ref="cacheManager"/>
    </bean>
    <!--3、配置Shiro核心过滤器-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!-- Shiro的核心安全接口,这个属性是必须的 -->
        <property name="securityManager" ref="securityManager" />
        <!-- 身份验证失败,跳转到登录页面 -->
        <property name="loginUrl" value="/home/index.html"/>
        <!-- 身份验证成功,跳转到指定页面 -->
       <!-- <property name="successUrl" value="/index.jsp"/>
        &lt;!&ndash; 权限验证失败,跳转到指定页面 &ndash;&gt;
        <property name="unauthorizedUrl" value="/noauthorizeUrl.jsp"/>-->
        <!-- Shiro连接约束配置,即过滤链的定义 -->
        <property name="filterChainDefinitions">
            <value>
                <!--anon 表示匿名访问,不需要认证以及授权-->
                <!--authc表示需要认证 没有进行身份认证是不能进行访问的-->
                /user/userLogin=anon
                /book/**=authc
                <!-- /css/**               = anon
                 /images/**            = anon
                 /js/**                = anon
                 /                     = anon
                 /user/logout          = logout
                 /user/**              = anon
                 /userInfo/**          = authc
                 /dict/**              = authc
                 /console/**           = roles[admin]
                 /**                   = anon-->
            </value>
        </property>
    </bean>
 
    <!--4、配置Shiro的生命周期-->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/></bean>
 
</beans>


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <!--引入mybatis与spring的集成配置-->
        <import resource="spring-mybatis.xml"/>
        <!--引入shiro与spring的集成配置-->
        <import resource="spring-shiro.xml"/>
</beans>

7.创建logincontorller

@Controller
@RequestMapping("/user")
public class LoginController {
 
    @Autowired
    private ISysUserService ISysUserService;
    /**
     * 跳转到登录页面
     * @return
     */
    @RequestMapping("tologin")
    public String toLogin(){
        return "login";
    }
 
    /**
     * 登录方法
     * @return
     */
    @RequestMapping("/userLogin")
    public String userLogin(SysUser sysuser, Model model){
        /*if("admin".equals(username)|| password.equals("123")){
           session.setAttribute("username",username);
            return "redirect:/";
        }
        model.addAttribute("msg","账号或者密码错误");
        return "login";*/
 
         //获取主体
        Subject subject = SecurityUtils.getSubject();
        //创建账号密码令牌
        UsernamePasswordToken token = new UsernamePasswordToken(
                    sysuser.getUsername(),
                    sysuser.getPassword()
                );
 
 
        //定义错误信息
        String msg=null;
        try {
            subject.login(token);
        }catch (UnknownAccountException e) {
            msg="账号错误";
            e.printStackTrace();
        }catch (IncorrectCredentialsException e) {
            msg="密码错误";
            e.printStackTrace();
        }catch (AuthenticationException e) {
            msg="账号或密码错误";
            e.printStackTrace();
        }
 
        //判断msg
        if(msg == null){
            return "redirect:/toIndex";
        }else{
            model.addAttribute("msg",msg);
            return "login";
        }
 
    }
 
    /**
     * 安全退出
     * @return
     */
    @RequestMapping("/userLogout")
    public String userLogout(){
        //获取主体
        Subject subject = SecurityUtils.getSubject();
        subject.logout();;
        return "redirect:/home/index.html";
    }
}

8.登录页面login.jsp

<body>
    <h1>登录</h1> <h2>${msg}</h2>
<form action="${ctx}/user/userLogin" method="post">
    账号:<input type="text" name="username"><br>
    密码:<input type="text" name="password"><br>
    <input type="submit" value="登录">
</form>
</body>

盐加密

1.什么是盐加密
盐加密是一种对系统登录口令的加密方式,它实现的方式是将每一个口令同一个叫做”盐“(salt)的n位随机数相关联。无论何时只要口令改变,随机数就改变。随机数以未加密的方式存放在口令文件中,这样每个人都可以读。不再只保存加密过的口令,而是先将口令和随机数连接起来然后一同加密,加密后的结果放在口令文件中。

2.使用盐加密
1.导入工具类PasswordHelper

/**
 * 用于shiro权限认证的密码工具类
 */
public class PasswordHelper {
 
    /**
     * 随机数生成器
     */
    private static RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();
 
    /**
     * 指定hash算法为MD5
     */
    private static final String hashAlgorithmName = "md5";
 
    /**
     * 指定散列次数为1024次,即加密1024次
     */
    private static final int hashIterations = 1024;
 
    /**
     * true指定Hash散列值使用Hex加密存. false表明hash散列值用用Base64-encoded存储
     */
    private static final boolean storedCredentialsHexEncoded = true;
 
    /**
     * 获得加密用的盐
     *
     * @return
     */
    public static String createSalt() {
        return randomNumberGenerator.nextBytes().toHex();
    }
 
    /**
     * 获得加密后的凭证
     *
     * @param credentials 凭证(即密码)
     * @param salt        盐
     * @return
     */
    public static String createCredentials(String credentials, String salt) {
        SimpleHash simpleHash = new SimpleHash(hashAlgorithmName, credentials,
                salt, hashIterations);
        return storedCredentialsHexEncoded ? simpleHash.toHex() : simpleHash.toBase64();
    }
 
 
    /**
     * 进行密码验证
     *
     * @param credentials        未加密的密码
     * @param salt               盐
     * @param encryptCredentials 加密后的密码
     * @return
     */
    public static boolean checkCredentials(String credentials, String salt, String encryptCredentials) {
        return encryptCredentials.equals(createCredentials(credentials, salt));
    }
 
    public static void main(String[] args) {
        //盐
        String salt = createSalt();
        System.out.println("随机生成的盐:"+salt);
        System.out.println("盐的长度:"+salt.length());
        //凭证+盐加密后得到的密码
        String credentials = createCredentials("123456", salt);
        System.out.println("凭证+盐加密后得到的密码:"+ credentials);
        System.out.println("凭证+盐加密后得到的密码的长度:"+ credentials.length());
        boolean b = checkCredentials("123456", salt, credentials);
        System.out.println("输入的密码是否能够通过盐+密码的验证:"+b);
 
    }
}

 

3.把生成的盐与密码加入数据库

 

4.在自定义ShrioRealm中的密码校验加上盐

//创建SimpleAuthenticationInfo,传入正确的账号和密码(来自于数据库)
        SimpleAuthenticationInfo simple=new SimpleAuthenticationInfo(
                sysUser.getUsername(),
                sysUser.getPassword(),
                //盐
                ByteSource.Util.bytes(sysUser.getSalt()),
                this.getName()
        );

5.去spring-shrio.xml中指明自定义ShrioRealm用的

<!--1、注册自定义的Realm 采用数据库方式-->
    <bean id="shiroRealm" class="com.zking.ssm.book.shiro.ShiroRealm">
        <!--配置Shiro明文密码如何进行加密-->
        <!--注意:重要的事情说三次~~~~~~此处加密方式要与用户注册时的算法一致 -->
        <!--注意:重要的事情说三次~~~~~~此处加密方式要与用户注册时的算法一致 -->
        <!--注意:重要的事情说三次~~~~~~此处加密方式要与用户注册时的算法一致 -->
        <!--以下三个配置告诉shiro将如何对用户传来的明文密码进行加密-->
        <property name="credentialsMatcher">
            <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <!--指定hash算法为MD5-->
                <property name="hashAlgorithmName" value="md5"/>
                <!--指定散列次数为1024次-->
                <property name="hashIterations" value="1024"/>
                <!--true指定Hash散列值使用Hex加密存. false表明hash散列值用用Base64-encoded存储-->
                <property name="storedCredentialsHexEncoded" value="true"/>
            </bean>
        </property>
    </bean>

6.再次登录依然成功进入首页

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值