通常のディレクトリ
レビュー
前回のブログでは、ログインと登録のインターフェースを作成しましたが、まだ問題がありますが、このブログでは、ログインと登録について詳しく説明します。
データ転送オブジェクト(DTO)
ログインまたは登録するとき、実際にはUserオブジェクトの一部の属性のみを使用します。他のユーザーにアクセスするときは、他のユーザーのパスワードや他のフィールドを照会しないでください。現時点では、すべてのユーザーオブジェクトを使用している場合は生成されます。冗長フィールドの一部であるこれらの冗長部分は、前に述べたようにデータ伝送の効率を低下させるため、最初にここから、さまざまな状況で使用する必要があるDTOを作成します。
ログインおよび登録機能用のDTOを作成する
UserLoginDTO
ユーザーがログインするとき、ユーザーはユーザー名とパスワードを入力するだけでよいので、UserLoginDTOには2つのフィールドしかありません。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserLoginDTO {
@NotNull(message = "账号不允许为空")
private String username;
@NotNull(message = "密码不允许为空")
private String password;
}
登録するとき、ユーザーはユーザー名、パスワード、およびニックネームを入力して登録を完了するだけです。
UserRegisterDTO
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserRegisterDTO {
@NotNull(message = "用户名不允许为空")
private String username;
@NotNull(message = "密码不允许为空")
private String password;
@NotNull(message = "昵称不允许为空")
private String nickname;
}
2つのフィールドの書き込みが完了したら、コントローラーレイヤーとサービスレイヤーに移動してコードを変更し、最初に受け入れたUserオブジェクトを対応するDTOオブジェクトに変換します
UserControllerを変更する
@PostMapping("/register")
public CommonResult<String> register(@RequestBody UserRegisterDTO urDTO) {
userService.userRegister(urDTO);
return new CommonResult<>(20000, "OK", "注册成功");
}
@PostMapping("/login")
public CommonResult<String> login(@RequestBody UserLoginDTO ulDTO) {
String res = userService.userLogin(ulDTO);
return new CommonResult<>(20000, "OK", res);
}
UserServiceを変更する
public void userRegister(UserRegisterDTO urDTO) {
User user = new User();
BeanUtils.copyProperties(urDTO,user);
try {
user.setId(idWorker.nextId() + "");
user.setEnable(1);
user.setAddtime(DateTimeTransferUtil.getNowTimeStamp());
userDao.userRegister(user);
} catch (Exception e) {
throw new DuplicateKeyException("用户名重复");
}
}
public String userLogin(UserLoginDTO ulDTO) {
User trueUser = userDao.userLogin(ulDTO.getUsername());
if (trueUser.getPassword().equals(ulDTO.getPassword())){
return "登陆成功";
}
throw new InternalAuthenticationServiceException("用户名或密码有误");
}
userRegisterメソッドBeanUtils.copyProperties
では、Springが提供するメソッドであるメソッドを使用します。これは、DTOから値をすばやく読み取ってpojoに注入したり、その逆を行ったりするのに役立ちます。このメソッドについては、インターネットで確認できます。データ。
次に、受信pojoから受信DTOへの変換を完了しましたが、それだけが問題ですか?このブログ設定のユーザー権限の次の部分に行きましょう
ユーザー初期許可設定
以前のブログでは、このプロジェクトの権限の確認は主にトークンを介して行われると述べましたが、前回作成したコードでは、登録時にユーザーに初期権限を付与することも、ユーザーがログインするときに権限を生成することもありませんでした。トークンはフロントエンドに返されます。これについては、このセクションでゆっくり説明します。
初期許可設定
ユーザーが正常に登録されたら、ユーザーの初期認証を完了する必要があるため、対応するメソッドをUserDaoに追加して、この機能を完了させます。
初期権限を追加し、ユーザーロールをクエリする
/**
* 用户注册时分配用户角色
*
* @param uid 用户id
*/
@Insert("INSERT INTO tb_role(uid,rolename) VALUES(#{uid},default)")
void allocateUserRole(@Param("uid") String uid);
/**
* 通过用户id查询用户角色
*
* @param id 用户id
* @return 用户角色
*/
@Select("SELECT rolename FROM tb_role WHERE uid = #{id}")
String getUserRole(@Param("id") String id);
ここでは、データベースのtb_roleテーブルのデフォルトのロール名をMEMBERに設定しているため、デフォルトを使用します。
初期権限を付与する
関連するメソッドをUserDaoに追加した後、UserServiceに注意を向けました。
public void userRegister(UserRegisterDTO urDTO) {
User user = new User();
BeanUtils.copyProperties(urDTO,user);
try {
user.setId(idWorker.nextId() + "");
user.setEnable(1);
user.setAddtime(DateTimeTransferUtil.getNowTimeStamp());
userDao.userRegister(user);
userDao.allocateUserRole(user.getId());
} catch (Exception e) {
throw new DuplicateKeyException("用户名重复");
}
}
ユーザーの登録後、ユーザーの役割allocateUserRole()
を追加するメソッドが実行され、登録の初期認証が完了します。まずプログラムを実行し、PostManを使用してテストします[最初にデータベース内の同じ名前のデータを削除します!]:
正常に登録するように求められ、データベースに移動してtb_userテーブルとtb_roleテーブルを確認します。ユーザーが正常に登録し、デフォルトのMEMBERロールを割り当てたことがわかりますが、これは本当に完了していますか?UserServiceのuserRegisterメソッドに戻り、次のコードを追加します
//请先将try catch的有关代码注释
userDao.userRegister(user);
//在两行代码中插入下面这一行代码
int i = 1 / 0;
userDao.allocateUserRole(user.getId());
明らかにエラーが発生するので、プログラムを実行して、登録インターフェースをもう一度見てみましょう。
この時点でデータベースを開いたところ、データベース内のtest1という名前のユーザーが正常に登録されていることがわかりましたが、エラーのため、ロールを割り当てることができませんでした。
トランザクション管理を有効にする
この状況は明らかに発生したくないものです。現在のように半分成功して半分失敗するのではなく、一緒に成功または失敗することを願っています。現時点では、@Transactional(rollbackForClassName = "Exception.class")
このアノテーションをSpringフレームワークからUserServiceクラスに追加して、トランザクション機能を有効にし、指定したタイプのエラーが発生したときにロールバックします(ここでは、ロールバックする例外を指定しています)。プロジェクトを再起動し、登録したばかりのtest1を削除して、同じデータで登録します。現時点では、エラーは発生しましたが、どちらのステップも有効にならず、成功または失敗の実現が同時に完了していることがわかります。[テストが完了したら、コメント化されたTryCatchコードのコメントを解除することを忘れないでください]
この時点で、ユーザーの初期権限設定が完了しているので、ユーザーログインモジュールを改善します
ユーザーログインモジュールを改善する
前述のとおり、ユーザーがログインに成功したら、ユーザーのIDと権限名を使用してトークンを生成し、フロントエンドに戻る必要があります。次に、この関数を完了します。まず、UserDaoに移動して、userLoginメソッドを次のように変更する必要があります。
/**
* 通过登录的账号查询是否存在该用户
*
* @param loginName 登录名,可能是username也可能是email
* @return 返回用户密码
*/
@Select("SELECT id,password FROM tb_user WHERE username = #{loginName}")
User userLogin(@Param("loginName") String loginName);
次に、UserServiceを次のように変更します。
public String userLogin(UserLoginDTO ulDTO) {
User trueUser = userDao.userLogin(ulDTO.getUsername());
if (trueUser.getPassword().equals(ulDTO.getPassword())){
return jwtUtil.createJWT(trueUser.getId(),userDao.getUserRole(trueUser.getId()));
}
throw new InternalAuthenticationServiceException("用户名或密码有误");
}
必要に応じてユーザーIDとユーザーロールで作成されたトークンを返し、このトークンをコントローラーの統合リクエストの戻り本文に含めます。
@PostMapping("/login")
public CommonResult<String> login(@RequestBody UserLoginDTO ulDTO) {
String token = userService.userLogin(ulDTO);
return new CommonResult<>(20000, "OK", toekn);
}
これで、トークンをフロントエンドに戻す作業も完了しました。ユーザーログインモジュールを改善する作業も終わりました。登録とログイン機能全体を振り返ってみましょう。これで比較的完成しましたが、まだいくつかの問題があります。登録プロセスをさらに改善しましょう。
ユーザーの個人情報を保護!
インターネットの発達に伴い、ユーザーパスワードの漏洩が時々発生するというニュースがあります。外部からの攻撃であれ、内部からのスタッフの漏洩であれ、ビジネスロジックを作成するプロセスであれ、注意が必要です。または、ユーザー情報の保存プロセスに注意する必要があります。現在、プロジェクトのユーザーパスワードはプレーンテキストで保存されています。ユーザーパスワードを暗号化するには、何らかの暗号化方法を使用する必要があります。リークもクラックできません。
では、どのように暗号化すればよいでしょうか?SpringSecurityフレームワークに付属するBCryptPasswordEncoderクラスを使用して、個人情報を暗号化できます。
ここでは、暗号化に関連するアルゴリズムについては説明しませんが、興味がある場合は、SpringSecurityの公式ドキュメントにアクセスして、学習と研究を行うことができます。
具体的な利用方法は以下の通りです。まず、このクラスをメインのスタートアップクラスに登録します。
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
次に、UserServiceで次の変更を行います。
最初に、構築メソッドを通じてこの暗号化クラスを注入します
private final UserDao userDao;
private final IdWorker idWorker;
private final JwtUtil jwtUtil;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
public UserService(UserDao userDao, IdWorker idWorker, JwtUtil jwtUtil, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userDao = userDao;
this.idWorker = idWorker;
this.jwtUtil = jwtUtil;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
次に、ユーザーが登録するときにユーザーのパスワードを暗号化します
public void userRegister(UserRegisterDTO urDTO) {
User user = new User();
BeanUtils.copyProperties(urDTO,user);
try {
user.setId(idWorker.nextId() + "");
user.setEnable(1);
user.setAddtime(DateTimeTransferUtil.getNowTimeStamp());
//进行加密
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
userDao.userRegister(user);
userDao.allocateUserRole(user.getId());
} catch (Exception e) {
throw new DuplicateKeyException("用户名重复");
}
}
ユーザーがログインするとき、ユーザーのパスワードがデータベースに保存されているパスワードと同じかどうかを判断する必要がありますが、登録時に保存されたパスワードは暗号化されているため、どのように判断するのですか?実際、フレームワークの作成者はこれをすでに考えておりmatches()
、判断する方法を提供してくれました。同じことが当てはまる場合、違いが偽の場合は、ユーザーログインモジュールを変更します。
public String userLogin(UserLoginDTO ulDTO) {
User trueUser = userDao.userLogin(ulDTO.getUsername());
//前面放的是用户输入的密码,后面是加密过的密码
if (bCryptPasswordEncoder.matches(ulDTO.getPassword(), trueUser.getPassword())) {
return jwtUtil.createJWT(trueUser.getId(), userDao.getUserRole(trueUser.getId()));
}
throw new InternalAuthenticationServiceException("用户名或密码有误");
}
したがって、ここでユーザーのプライバシー情報の暗号化を完了し、プロジェクトを開始し、postmanを介して2つの機能をテストしましょう〜
まず、登録インターフェースをテストします(テスト前に以前のテストアカウントを削除することを忘れないでください!)、登録が成功し、データベースにアクセスすると、ユーザーのパスワードが暗号化されていることがわかります!
次に、ログインインターフェイスをテストします。
ログインが成功し、生成されたトークンが正常に取得されていることもわかりました。トークンのフロントエンドを使用すると、要求するたびにユーザーのIDと権限を直接取得できるため、後続のビジネスコードの記述を強力にサポートできます。
これまでのところ、このブログの内容は基本的に終了しており、このブログ以降、ログインと登録のインターフェースは比較的完璧になりましたが、本当に何の問題もないのでしょうか。読者は自分で考えてください、そして著者は次のブログで答えを明らかにします。
このブログの内容はここで終了しました。ブログの内容について質問がある場合は、プライベートメッセージで作成者に連絡できます。この記事がお役に立てれば幸いです。よろしくお願いします〜