オンラインの研究 - 17日目 - 講義 - ユーザー認証Zuul

ユーザー認証 
1.1  ユーザー認証プロセスの分析 
、次のようにユーザ認証プロセスは次のとおりです。
 
業務プロセス、次のように: 
1 、クライアントは、認証サービスの認証を要求します。 
2 ブラウザによって認定、認証業務クッキーが書かれている(トークンIDトークン) 
認証サービスセンターは、利用者情報を照会するユーザーを要求します。 
認証サービス要求春のセキュリティアプリケーショントークン。 
認証サービスのトークン(IDトークンJWT にトークンストレージRedisの中で。 
認定サービスクッキーの書き込み  トークン(IDトークン。 
3 、フロントエンドは、担持トークン認証サービス取得の要求JWT トークン 
遠位取得JWT トークンに格納されているのsessionStorage 。 
遠位JWT 解析トークンとユーザー情報がページに表示されます。 
4 、搬送フロントクッキーにおけるトークントークンと同一JWT リソースサービスにアクセスするためのトークン 
フロントエンドサービス要求リソースは、2つの搬送に必要なトークンを、あるクッキーIDトークンで、aは、HTTPヘッダJWT トークン 
フロントエンドサービスリソース要求前にHTTPヘッダーを追加するJWT リソース要求 
5をチェックゲートウェイ、トークンの正当性

運ぶ必要があり、ユーザーの要求をトークンIDをトークンとJWT トークン 
ゲートウェイチェックはRedisのトークンが、それは、ユーザーが再度ログインする必要があり、期限が切れてい正当である  6 、リソースサービスチェックJWT 正当性をと承認の完了 リソースのサービスチェックをJWT

トークン、認証を完了するために、メソッドが正常に実行する権限を持っている、アクセス許可を拒否する方法はありません。 
1.2  認証サービスは、データベース照会 
1.2.1  要求分析の 
口座番号とパスワードが一致を確認し、あるユーザーの身元を確認するために、データベース内のユーザ情報に基づいて認証サービスを、。 
認証サービスは、データベースに直接に接続されているが、ユーザへのユーザ中心のサービスを介して中央データベースに照会しません。
 

1.2.2  ビルド環境 
1.2.2.1  の作成、ユーザーを中心としたデータベースの 
ように、ユーザ情報管理、ロール管理、著作権管理や:などのユーザー管理を担当ユーザーセンター。 
作成xc_userのデータベース(MySQLの) 
輸入(xc_user.sql 輸入重複せずにインポートされている

 
1.2.2.2  の作成ユーザーセンタープロジェクト 
のインポートデータ」 - " xc-service-ucenter.zip
 

1.2.4  クエリのユーザインタフェースは、 
アカウントセンターによると、ユーザーを照会するユーザーを完了するために情報インタフェース機能。 
1.2.4.1アピインタフェース 
ユーザーは、外部インターフェイスを提供し、以下: 
1 、データの種類に応じて

このインターフェースはので、ここで定義された拡張タイプ、ユーザー情報やユーザー権限情報の未来を照会するために使用されます

@Data
@ToString
public class XcUserExt extends XcUser {
//权限信息
private List<XcMenu> permissions;
//企业信息
private String companyId;
}

2 アカウントのユーザ情報の問い合わせに応じて、

@Api(value = "用户中心",description = "用户中心管理")
public interface UcenterControllerApi {
public XcUserExt getUserext(String username);
}

1.2.4.2 DAO 
添加XcUserXcCompantUser两个表的Dao

public interface XcUserRepository extends JpaRepository<XcUser, String> {
XcUser findXcUserByUsername(String username);
}
public interface XcCompanyUserRepository extends JpaRepository<XcCompanyUser,String> {
//根据用户id查询所属企业id
XcCompanyUser findByUserId(String userId);
}

1.2.4.2 Service

@Service
public class UserService {
@Autowired
private XcUserRepository xcUserRepository;
//根据用户账号查询用户信息
public XcUser findXcUserByUsername(String username){
return xcUserRepository.findXcUserByUsername(username);
}
//根据账号查询用户的信息,返回用户扩展信息
public XcUserExt getUserExt(String username){
XcUser xcUser = this.findXcUserByUsername(username);
if(xcUser == null){
return null;
}
XcUserExt xcUserExt = new XcUserExt();
BeanUtils.copyProperties(xcUser,xcUserExt);
//用户id
String userId = xcUserExt.getId();
//查询用户所属公司
XcCompanyUser xcCompanyUser = xcCompanyUserRepository.findXcCompanyUserByUserId(userId);
if(xcCompanyUser!=null){
String companyId = xcCompanyUser.getCompanyId();
xcUserExt.setCompanyId(companyId);
}
return xcUserExt;
}
}

1.2.4.3 Controller

@RestController
@RequestMapping("/ucenter")
public class UcenterController implements UcenterControllerApi {
@Autowired
UserService userService;
@Override
@GetMapping("/getuserext")
public XcUserExt getUserext(@RequestParam("username") String username) {
XcUserExt xcUser = userService.getUserExt(username);
return xcUser;
}
}

1.2.4.4 测试 
使用Swagger-uipostman测试用户信息查询接口 
1.2.5 调用查询用户接口 
1.2.5.1 创建client 
认证服务需要远程调用用户中心服务查询用户,在认证服务中创建Feign客户端

@FeignClient(value = XcServiceList.XC_SERVICE_UCENTER)
public interface UserClient {
@GetMapping("/ucenter/getuserext")
public XcUserExt getUserext(@RequestParam("username") String username)
}

1.2.5.2 UserDetailsServiceImpl
认证服务调用spring security接口申请令牌,spring security接口会调用UserDetailsServiceImpl从数据库查询用户,如果查询不到则返回 NULL,表示不存在;在UserDetailsServiceImpl中将正确的密码返回, spring security会自动去比对输入密码的正确性。
1、修改UserDetailsServiceImplloadUserByUsername方法,调用Ucenter服务的查询用户接口

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
UserClient userClient;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//取出身份,如果身份为空说明没有认证
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
//没有认证统一采用httpbasic认证,httpbasic中存储了client_id和client_secret,开始认证
client_id和client_secret
if(authentication==null){
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(username);
if(clientDetails!=null){
//密码
String clientSecret = clientDetails.getClientSecret();
return new
User(username,clientSecret,AuthorityUtils.commaSeparatedStringToAuthorityList(""));
}
}
if (StringUtils.isEmpty(username)) {
return null;
}
//请求ucenter查询用户
XcUserExt userext = userClient.getUserext(username);
if(userext == null){
//返回NULL表示用户不存在,Spring Security会抛出异常
return null;
}
//从数据库查询用户正确的密码,Spring Security会去比对输入密码的正确性
String password = userext.getPassword();
String user_permission_string = "";
UserJwt userDetails = new UserJwt(username,
password,
AuthorityUtils.commaSeparatedStringToAuthorityList(user_permission_string));
//用户id
userDetails.setId(userext.getId());
//用户名称
userDetails.setName(userext.getName());
//用户头像
userDetails.setUserpic(userext.getUserpic());
//用户所属企业id
userDetails.setCompanyId(userext.getCompanyId());
return userDetails;
}
}

2、测试,请求http://localhost:40400/auth/userlogin 
观察UserDetailsServiceImpl是否正常请求Ucenter的查询用户接口。 
1.2.5.3 BCryptPasswordEncoder 
早期使用md5对密码进行编码,每次算出的md5值都一样,这样非常不安全,Spring Security推荐使用 
BCryptPasswordEncoder对密码加随机盐,每次的Hash值都不一样,安全性高。 
1BCryptPasswordEncoder测试程序如下

@Test
public void testPasswrodEncoder(){
String password = "111111";
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
for(int i=0;i<10;i++) {
//每个计算出的Hash值都不一样
String hashPass = passwordEncoder.encode(password);
System.out.println(hashPass);
//虽然每次计算的密码Hash值不一样但是校验是通过的
boolean f = passwordEncoder.matches(password, hashPass);
System.out.println(f);
}
}

2、在AuthorizationServerConfifig配置类中配置BCryptPasswordEncoder

//采用bcrypt对密码进行Hash
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

3、测试 
请求http://localhost:40400/auth/userlogin,输入正常的账号和密码进行测试 
1.2.5.4 解析申请令牌错误信息 
当账号输入错误应该返回用户不存在的信息,当密码错误要返回用户名或密码错误信息,业务流程图如下:
 

修改申请令牌的程序解析返回的错误
由于restTemplate收到400401的错误会抛出异常,而spring security针对账号不存在及密码错误会返回400及 
401,所以在代码中控制针对400401的响应不要抛出异常。

......
Map map = null;
try {
((RestTemplate) restTemplate).setErrorHandler(new DefaultResponseErrorHandler() {
@Override
public void handleError(ClientHttpResponse response) throws IOException {
// 设置 当响应400和401时照常响应数据,不要报错
if (response.getRawStatusCode() != 400 && response.getRawStatusCode() != 401 ) {
super.handleError(response);
}
}
});
//http请求spring security的申请令牌接口
ResponseEntity<Map> mapResponseEntity = restTemplate.exchange(path, HttpMethod.POST, new
HttpEntity<MultiValueMap<String, String>>(formData, header), Map.class);
map = mapResponseEntity.getBody();
} catch (Exception e) {
e.printStackTrace();
LOGGER.error("request oauth_token_password error: {}",e.getMessage());
e.printStackTrace();
ExceptionCast.cast(AuthCode.AUTH_LOGIN_APPLYTOKEN_FAIL);
}
if(map == null ||
map.get("access_token") == null ||
map.get("refresh_token") == null ||
map.get("jti") == null){//jti是jwt令牌的唯一标识作为用户身份令牌
//获取spring security返回的错误信息
String error_description = (String) map.get("error_description");
if(StringUtils.isNotEmpty(error_description)){
if(error_description.equals("坏的凭证")){
ExceptionCast.cast(AuthCode.AUTH_CREDENTIAL_ERROR);
}else if(error_description.indexOf("UserDetailsService returned null")>=0){
ExceptionCast.cast(AuthCode.AUTH_ACCOUNT_NOTEXISTS);
}
}
ExceptionCast.cast(AuthCode.AUTH_LOGIN_APPLYTOKEN_FAIL);
}
......


 
1.2.5.5 测试 
使用postman请求http://localhost:40400/auth/userlogin 
1、输入正确的账号和密码进行测试 
从数据库找到测试账号,本课程所提供的用户信息初始密码统一为111111 
2、输入错误的账号和密码进行测试 

发布了835 篇原创文章 · 获赞 152 · 访问量 14万+

おすすめ

転載: blog.csdn.net/qq_40208605/article/details/104393705