要約:
商品を購入するユーザーを必要とするか、または:、主に使用し、ユーザーのログイン認証機能を実装し、認証フレームワーク史郎 - このポストは、第五章では、この記事では、我々は証明機関を統合する「の記事のシリーズは、Javaのスパイク戦闘システム」でありますときコモディティスパイク、ログインするユーザーを制限します!特定のURLをフィルタする(例えば購入URLとして、要求に対応する)(すなわち、指定されたURLへのアクセス要求ユーザのログインが必要な場合)。
コンテンツ:
史郎のために、私はあなたが小さなパートナーを聞いたことがあるはずです、あるいは使用されるべきであると信じて!簡単に言えば、それはユーザーのID認証、許可特権フレームワーク、ユーザログイン認証を有効にする、権限、リソース許可、セッション管理やその他の機能を十分に活用、システムでこのスパイクで、我々はユーザーを達成するためのフレームワークを採用します認証およびユーザーログイン機能のID。
「史郎は、ユーザのログイン認証を実装する」この記事は、ユーザ情報テーブルのユーザーのためのデータベーステーブルに関連した機能モジュールを説明し、次のコードを入力すると、実際のリンクである、ことを言及する価値があります。
<! -史朗权限控制- >
<依存>
<groupIdを> org.apache.shiro </ groupIdを>
<たartifactId>史郎-にehcache </たartifactId>
<バージョン> $ {shiro.version} </バージョン>
</依存>
<依存性>
<のgroupId> org.apache.shiro </のgroupId>
<たartifactId>シロコア</たartifactId>
<バージョン> $ {shiro.version} </バージョン>
</依存>
<依存性>
<のgroupId> org.apache.shiro </のgroupId>
<たartifactId>シロウェブ</たartifactId>
<バージョン> $ {shiro.version} </バージョン>
</依存>
<依存性>
<のgroupId> ORG。apache.shiro </のgroupId>
<たartifactId>シロスプリング</たartifactId>
<バージョン> $ {shiro.version} </バージョン>
</依存>
(2)UserControllerでコントローラにログインするユーザの開発に続いて、ユーザのログオン要求に対応する方法の機能のうち、ユーザログは、完全なソースコードは以下の通り:
@Autowired
プライベート環境ENV;
// 跳到登录页
@RequestMapping(値= { "/ /ログインする"、 "/ UNAUTH" })
パブリック文字列toLogin(){
戻り "ログイン" 。
}
// 登录认证
@RequestMapping(値= "/ログイン"、メソッド= RequestMethod.POST)
パブリック文字列ログイン(@RequestParam文字列ユーザ名、@RequestParam文字列のパスワード、ModelMap modelMap){
文字列errorMsg内容 = "" ;
してみてください{
場合(!SecurityUtils.getSubject()。にisAuthenticated()){
文字列newPsd = 新しいですMd5Hash(パスワード、env.getProperty( "shiro.encrypt.password.salt" 。))のtoString();
UsernamePasswordTokenトークン = 新しいUsernamePasswordToken(ユーザ名、newPsd)。
SecurityUtils.getSubject()ログイン(トークン)。
}
} キャッチ(UnknownAccountException電子){
errorMsg内容 = e.getMessage()。
modelMap.addAttribute( "userNameに" ユーザ名)。
} キャッチ(DisabledAccountException電子){
errorMsg内容 = e.getMessage()。
modelMap.addAttribute( "userNameに" ユーザ名)。
} キャッチ(IncorrectCredentialsException E)は{
errorMsg内容 = e.getMessage();
modelMap.addAttribute( "userNameに" ユーザ名);
} キャッチ(例外E){
errorMsg内容 = "ユーザログイン例外は、管理者に連絡してください!" ;
E.printStackTrace() ;
}
IF (StringUtils.isBlank(errorMsg内容)){
リターン "リダイレクト:/インデックス" ;
} 他{
modelMap.addAttribute( "errorMsg内容" 、errorMsg内容);
リターン "ログイン" ;
}
}
// ログ
(値= "/ログアウト" @RequestMapping )
パブリック文字列ログアウト(){
SecurityUtils.getSubject()ログアウト()。
リターン「ログイン」;
}
ユーザパスワードが一致は、Md5Hash方法は、我々がここで使用されるとき、請求項、すなわち、MD5暗号化方法は、(それはMD5暗号化されたパスワード文字列フィールドに格納ユーザーデータベース内のユーザテーブルを用いて暗号化されているため)、フロントエンドと一致しますlogin.jspをコンテンツページは、ページのソースコードのコア部分のために、以下のようにのみ、基本的なユーザー名とパスワードを入力するようユーザに要求する、比較的簡単です。
要求が「フォームを送信」する現在のエンド「ユーザログイン」は、後端に対応する方法UserControllerでコントローラにログインするためのユーザー名とパスワードの形式で提示される送信、処理は最初に、基本パラメータと決意をチェックしますチェックを通過した後、シロコールは、組み込みコンポーネントSecurityUtils.getSubject()。login()メソッドは、ログイン操作は、主に「doGetAuthenticationInfo方法のカスタムレルム」で行われる、請求ログイン操作を行います。
(3)それは史郎さんAuthorizingRealmに基づいて次に、カスタム開発レルム、およびユーザーのログイン認証方式、すなわちdoGetAuthenticationInfo()メソッドを実装します。その完全なソースコードは次のよう:
/**
* 用户自定义的realm-用于shiro的认证、授权
* @Author:debug (SteadyJack)
* @Date: 2019/7/2 17:55
**/
public class CustomRealm extends AuthorizingRealm{
private static final Logger log= LoggerFactory.getLogger(CustomRealm.class);
private static final Long sessionKeyTimeOut=3600_000L;
@Autowired
private UserMapper userMapper;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//认证-登录
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token= (UsernamePasswordToken) authenticationToken;
String userName=token.getUsername();
String password=String.valueOf(token.getPassword());
log.info("当前登录的用户名={} 密码={} ",userName,password);
User user=userMapper.selectByUserName(userName);
if (user==null){
throw new UnknownAccountException("用户名不存在!");
}
if (!Objects.equals(1,user.getIsActive().intValue())){
throw new DisabledAccountException("当前用户已被禁用!");
}
if (!user.getPassword().equals(password)){
throw new IncorrectCredentialsException("用户名密码不匹配!");
}
SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(user.getUserName(),password,getName());
setSession("uid",user.getId());
return info;
}
/**
* 将key与对应的value塞入shiro的session中-最终交给HttpSession进行管理(如果是分布式session配置,那么就是交给redis管理)
* @param key
* @param value
*/
private void setSession(String key,Object value){
Session session=SecurityUtils.getSubject().getSession();
if (session!=null){
session.setAttribute(key,value);
session.setTimeout(sessionKeyTimeOut);
}
}
}
其中,userMapper.selectByUserName(userName);主要用于根据userName查询用户实体信息,其对应的动态Sql的写法如下所示:
<!--根据用户名查询-->
<select id="selectByUserName" resultType="com.debug.kill.model.entity.User">
SELECT <include refid="Base_Column_List"/>
FROM user
WHERE user_name = #{userName}
</select>
值得一提的是,当用户登录成功时(即用户名和密码的取值跟数据库的user表相匹配),我们会借助Shiro的Session会话机制将当前用户的信息存储至服务器会话中,并缓存一定时间!(在这里是3600s,即1个小时)!
(4)最后是我们需要实现“用户在访问待秒杀商品详情或者抢购商品或者任何需要进行拦截的业务请求时,如何自动检测用户是否处于登录的状态?如果已经登录,则直接进入业务请求对应的方法逻辑,否则,需要前往用户登录页要求用户进行登录”。基于这样的需求,我们需要借助Shiro的组件ShiroFilterFactoryBean 实现“用户是否登录”的判断,以及借助FilterChainDefinitionMap拦截一些需要授权的链接URL,其完整的源代码如下所示:
/**
* shiro的通用化配置
* @Author:debug (SteadyJack)
* @Date: 2019/7/2 17:54
**/
@Configuration
public class ShiroConfig {
@Bean
public CustomRealm customRealm(){
return new CustomRealm();
}
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
securityManager.setRealm(customRealm());
securityManager.setRememberMeManager(null);
return securityManager;
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(){
ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager());
bean.setLoginUrl("/to/login");
bean.setUnauthorizedUrl("/unauth");
//对于一些授权的链接URL进行拦截
Map<String, String> filterChainDefinitionMap=new HashMap<>();
filterChainDefinitionMap.put("/to/login","anon");
filterChainDefinitionMap.put("/**","anon");
filterChainDefinitionMap.put("/kill/execute","authc");
filterChainDefinitionMap.put("/item/detail/*","authc");
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return bean;
}
}
从上述该源代码中可以看出,Shiro的ShiroFilterFactoryBean组件将会对 URL=/kill/execute 和 URL=/item/detail/* 的链接进行拦截,即当用户访问这些URL时,系统会要求当前的用户进行登录(前提是用户还没登录的情况下!如果已经登录,则直接略过,进入实际的业务模块!),除此之外,Shiro的ShiroFilterFactoryBean组件还设定了 “前往登录页”和“用户没授权/没登录的前提下的调整页”的链接,分别是 /to/login 和 /unauth!
(5)至此,整合Shiro框架实现用户的登录认证的前后端代码实战已经完毕了,将项目/系统运行在外置的tomcat服务器中,打开浏览器即可访问进入“待秒杀商品的列表页”,点击“详情”,此时,由于用户还没登陆,故而将跳转至用户登录页,如下图所示:
输入用户名:debug,密码:123456,点击“登录”按钮,即可登录成功,并成功进入“详情页”,如下图所示:
登录成功之后,再回到刚刚上一个列表页,即“待秒杀商品的列表页”,再次点击“详情”按钮,此时会直接进入“待秒杀商品的详情页”,而不会跳转至“用户登录页”,而且用户的登录态将会持续1个小时!(这是借助Shiro的Session的来实现的)。
补充:
1、目前,这一秒杀系统的整体构建与代码实战已经全部完成了,完整的源代码数据库地址可以来这里下载:https://gitee.com/steadyjack/SpringBoot-SecondKill 记得Fork跟Star啊!!!
2、实战期间有任何问题都可以留言或者与Debug联系、交流;QQ技术交流群:605610429,顺便关注一下Debug的技术微信公众号呗: