当前位置:网站首页>Microservice architecture practice: business management background and SSO design: SSO design

Microservice architecture practice: business management background and SSO design: SSO design

2022-06-26 16:59:00 Seconds to write code

SSO Design

Spring Security Is a powerful 、 Customizable authentication and access control framework .Spring Security OAuth2 It's based on Spring The framework supports third-party application authorization tool components . By using Spring Security OAuth2, We can perform single sign on in the merchant background (SSO) Design , So as to provide system integration for multiple microservice applications , Use unified security control management .

SSO The design is divided into two parts: server and client .SSO The server provides unified access control and authorization authentication services for each application , It's a Web UI Microservice application , In the module merchant-sso Development in , Contains the user login design 、 Home page design and authentication service design .SSO A client is a program that provides local services to users

SSO Basic configuration

SSO Basic configuration and general Web UI The application project configuration is basically the same , That is to say Web UI Based on the application project configuration , increase Spring Cloud OAuth2 The dependent quotation of , The code is as follows :

<dependency>
<group Id>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

This component already contains Security and OAuth2 Two component systems , among ,Security Provides access control functions ,OAuth2 It provides third-party application authorization and authentication services .

In the application's configuration file application.yml in , Set up SSO Service port of the application , And set a cookie Save the user's login information , The code is as follows :

server:
port:8000
session:
cookie:
name: SESSIONID

To ensure that third-party applications can be accessed normally after authorization , We must add access to the configuration file SSO The client returns a list of addresses , And use the correct format “http:/l Domain name or IP: port /login” To set , The code is as follows ;

#SSo The client returns a list of addresses ssoclient:
redirecturis:
- http://localhost:8081/login- http://127.0.0.1:8081/1ogin

sso Third party application authorization design

To give access to SSO Third party applications for ( This refers to access SSO Other microservice applications for services ) To authorize , We created a configuration class AuthServerConfig, It inherited
AuthorizationServerConfigurerAdapter, The code is as follows :

@Configuration
@EnableAuthorizationServer
@EnableConfigurationProperties(clienturls.class)
public class authServerConfig extends AuthorizationServerConfigurerAdapter
CAutowired
private Clienturls clienturls;
@Override
public void configure (AuthorizationServerSecurityConfigurer oauthServer)throws Exception {
oauthServer.tokenKeyAccess( "permitAll ()")
.checkTokenAccess("isAuthenticated()");
@override
public void configure (ClientDetailsServiceConfigurer clients) throwsException {
clients.inMemory()
.withClient ( "ssoclient")
.secret(passwordEncoder ().encode ("ssosecret")).authorizedGrantTypes ("authorization_code").scopes ( "user_info")
.autoApprove(true)
.redirectUris(clienturls.getRedirecturis());
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();converter. setSigningKey ( "demoSige");
return converter;
}
aoverride
public void configure(AuthorizationServerEndpointsConfigurer endpoints)throws Exception {
endpoints.accessTokenConverter(jwtAccessTokenConverter());
}
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}

In the configuration class above , It includes the following main functions :

(1) Using annotations @EnableAuthorizationServer Turn on SSO The function of the server .(2) Rewrite the first configure, Turn on use Token The function of authorizing .

(3) Rewrite the second configure, Specify the form page authorization method for the client , namely authorization_code, And set the user name and password used for client authorization as ssoclient and ssosecret. meanwhile , Import the client return address list in the configuration , And set the client return address list as redirectUris. in addition , adopt autoApprove(true) Set to automatically confirm authorization , The steps that must be confirmed manually during client authorization are omitted .

(4) Rewrite the third configure, Use safe JwtAccessToken. Here is the use of the key , We simply use one text demoSige, If you want to use a safer way , You can use KeyStore Generate keys for configuration .

sso Login authentication design

The following provides a login interface , It is used to receive the user name, password and other information entered by the user , Implement user login operation . In login authentication , Use Spring Security Verify user name and password .

Create a MyUserDetails class , Realization Spring Security Of UserDetails, The code is as follows :

public class MyUserDetails implements UserDetails {
private string username;
private String password;
private Collection<? extends GrantedAuthority> authorities;
private User user;
public MyUserDetails(String username, String password, collection<? extendsGrantedAuthority>authorities, User user){
this.username = username;
this.password = password;
this.authorities = authorities;this.user = user;
}
...
}

In this way, we can import our customized user system , Provide to Spring Security Certified .

Create a MyUserDetailsService class , Realization Spring Security Of UserDetailsService, Provide user information and import user roles for the following authentication services , The code is as follows :

@component
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserService userService;
@override
public UserDetails loadUserByUsername (String username) throwsUsernameNotFoundException {
User user = userService.findByName (username);
if(user==nul1){
throw new UsernameNotFoundException(" The user doesn't exist !");
}
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
List<Role> roles =user.getRoles ();
if(roles !=null)
{
for(Role role : roles) {
SimpleGrantedAuthority authority = new
SimpleGrantedAuthority(role.getName ());
authorities.add(authority);
}
}
MyUserDetails myUserDetails = new MyUserDetails (username,
user.getPassword(),authorities, user);
return myUserDetails;
}
}

such , When Spring Security When authenticating , We will call our users and get the relevant roles , This completes the user name and password verification at login , And configure corresponding permissions for users .

In order to make the above design effective , We also need to create a configuration class , namely SecurityConfng tip o Dagger inheritance 」SprngSecurity Of
WebSecurityConfigurerAdapter, The code is as follows :

@Configuration
public class SecurityConfig extends webSecurityConfigurerAdapter
@Autowired
private MyUserDetailsService myUserDetailsService;
@Autowired
@Qualifier("dataSource")
private DataSource dataSource;
@Bean(name = BeanIds.AUTHENTICATION MANAGER)Goverride
public AuthenticationManager authenticationManagerBean () throws Exception
return super.authenticationManagerBean();
@override
protected void configure (AuthenticationManagerBuilder auth)
throws Exception {
auth //lremember me
.eraseCredentials(false)
.userDetailsService (myUserDetailsService).passwordEncoder (new
BCryptPasswordEncoder());
@Override
protected void configure(HttpSecurity http) throws Exception
http.antMatcher("/**")
.authorizeRequests().antMatchers( "/login").permitAl1()
.antMatchers(" /images/**", "/checkcode", "/scripts/*
"/styles/**")
.permitAll().anyRequest().authenticated()
.and ().sessionManagement ().sessionCreationPolicy(SessionCreati
onPolicy.NEVER)
.and(.exceptionHandling().accessDeniedPage ("/deny")
.and().rememberMe ().tokenValiditySeconds (86400).tokenRepositor
y(tokenRepository())
.and()
.formLogin().loginPage ("/login" ).permitAll ().successHandler(lo
ginsuccessHandler())
.and() .logout()
.logoutUrl("/logout").permitAll().logoutSuccessUrl ("/signout");
}
@Bean
public JdbcTokenRepositoryImpl tokenRepository()
JdbcTokenRepositoryImpl jtr = new JdbcTokenRepositoryImpl()jtr.setDataSource(dataSource);
return jtr;
}
@Bean
public LoginSuccessHandler loginsuccessHandler(){
return new LoginSuccessHandler();
}
}

In this configuration class , The following settings are made :

(1) Set the user service as defined above MyUserDetailsService.

(2) Specify the login page link as /login, In this way, the view file can be specified through the controller design .

(3) Specify the handler for successful login loginSuccessHandler.

4) Ignore the verification of static resources such as images .

(5) Specify the error prompt link to deny access /deny.

(6) Use rememberMe() Set to remember the user's login status .

(7) Specifies that the local data source is used to store the temporary data of user login status .

Design for the above configuration class , We create a controller , Set up a login link /login, And specify an interface design page for it login.html, The code is as follows :

@Controller
public class LoginController {
@RequestMapping( "/1ogin")public String login(){
return "login";
)
}

On the interface design page login.html in , Mainly use a form design , Provide a login interface with user name, password and other input controls , The code is as follows :

...
<form th:action="@{/login}" id="loginForm" method="post">
<div class="loginTit png"></div>
<ul class="infList">
<li class="grayBox">
<label for="username" class="username-icon"></label><input id="username" class="username" name="username"
type="text" placeholder=" Your user name "/>
<div class="close png hide"></div></li>
<li class="grayBox">
<label class="pwd-icon" id="pwd"></label>
<input id="password" name="password" class="pwd"
type="password" placeholder=" The login password "/>
<div class="close png hide"></div></li>
<li class="" id="isCheckCode" style="display: none; ">
<label class="validateLabel" ></label>
<input id="checkCode" name=" checkCode" class="checkCo
type-"text" placeholder=" Verification Code "/>
<img onclick="reloadImg ();" style="cursor: pointer"
th:src="e{/images/imagecode)" id="validateImg" alt=" Verification Code "class="codePic"title-" Verification Code . Click here to update the verification code ."/>
<a class="getother" href="javascript:void(0);"
onclick="reloadImg ();" title=" Click here to update the verification code ."> to update </a>
</li>
</ul>
<ul class="infList reloadBtn" style="display: none; ">
<li>
<a href="javascript:void(0);" onclick="tologin();"> This page 
 It's invalid . Please click here to log in again .</a>
</li>
</u.l>
<divclass="loginBtnBo×">
div class="check-box"><input type="hidden" value="O"
id="remember-me" name="remember-me" onclick="if(this.checked) {this.value
1}else {this.value=0}"/>span class="toggleCheck no-check" id="repwd"></span> Remember me </div>
<input type="button" id="loginBtn" onclick="verSubmit()
value=" Sign in "class="loginBtn png"/>
</div>
</form>
...

Complete the design of the login interface , The display effect is similar to a floating window , Pictured 10-4 Shown .

Description of verification code

When the user logs in for the first time , You can't see the verification code ; When the user fails to log in for the first time and needs to log in again , Will be asked to enter the verification code .

About the implementation of verification code , Here are two main explanations , That is, the output of the verification code and the verification of the verification code .

The output of the verification code is an image , The code is as follows :

@RequestMapping (value = " /images/imagecode")
public String imagecode (HttpServletRequest request, HttpServletResponseresponse)
throws Exception {
OutputStream os = response.getOutputStream();
Map<String, 0bject> map = ImageCode.getImageCode(60,20,os);
String simpleCaptcha = "simpleCaptcha";
request.getSession().setAttribute(simpleCaptcha,map.get("strEnsure") .toString().toLowerCase());
request.getSession().setAttribute ("codeTime", new Date() .getTime());
try {
ImageIO.write ((BufferedImage)map.get ("image"),"JPEG",0S);]catch(IOException e){
return "";
}
return null;
}

That is, use... On the page “limages/imagecode” This link , You can return a verification code image consisting of several random numbers . When outputting the verification code , Use session Save the data of the verification code , Provide the basis for the verification code inspection in the next step .

About the verification of verification code , The implementation code is as follows :

@RequestMapping(value ="/checkcode")CResponseBody
public String checkcode (HttpServletRequest request,HttpSession session)
throws Exception {
String checkCode = request.getParameter("checkCode");
Object simple = session.getAttribute ("simpleCaptcha");// Verification code object if(simple == null){
request.setAttribute ("errorMsg"," The verification code has failed , Please re-enter !");return" The verification code has failed , Please re-enter !";
String captcha = simple.toString();Date now = new Date();
Long codeTime = Long.valueof(session.getAttribute ("codeTime")+"");if(StringUtils.isEmpty(checkCode) l captcha ==
null !(checkCode.equalsIgnoreCase(captcha))){
request.setAttribute( "errorMsg"," Verification code error !");return" Verification code error !";
}else if ((now.getTime ()-codeTime)/ 1000/60 >5)(// The valid length of the verification code is 5mi
request.setAttribute("errorMsg""," The verification code has failed , Please re-enter !");
return" The verification code has failed , Please re-enter !";
}else {
session.removeAttribute( "simpleCaptcha");return "1";
}
}

Match the user input with the above session Compare the saved data , If the same , It can pass the inspection .

After completing the above design , When the user fails to log in for the first time , It will display as shown in the figure 10-5 The login interface shown in .

ssO Home page design

If it is in an access SSO Login in the third-party application of the service , After successful login ,SSO The server will return to the relevant application according to the link address of the application . If it's in SSO Login in the server , By default, it returns SSO The home page of the server . In the homepage design , We provide links to other applications .

SSO Home page design , It is generally composed of two parts . The first part is the design of a controller , The implementation code is as follows :

@controller
public class LoginController {
@ReguestMapping ("/")
public String index (ModelMap model, Principal principal) throws Exception{
MyUserDetails myUserDetails = (MyUserDetails)
SecurityContextHolder.getContext () .getAuthentication().getPrincipal();
User user = myUserDetails.getUser();
// List of categories ( Top menu )
List<Kind> kindList =new ArrayList<>();List<Long> kindIds = new ArrayList<>();for(Role role : user.getRoles ()){
for(Resource resource : role.getResources()){
// duplicate removal , Get a list of categories 
Long kindId = resource.getModel().getKind().getId();if(! kindIds.contains(kindId)){
kindList.add(resource.getModel ().getKind());
kindIds.add (kindId);
}
}
}
model .addAttribute("kinds", kindList);
model . addAttribute("principal", principal);return "home";
}
}

First , adopt SecurityContextHolder Get the complete information of the login user . then , According to login user , Relationships around users , You can sort out the top-level menus that users can access . Last , Use the classification list kinds Make this top-level menu available to the home page view .

The second part is a page view design , Among them, the design of dealing with the classification list part , stay SSO Home page home.html The navigation part of , The code is as follows :

<div class="new-icon" th:each="kind: $ {kinds}">
<div class="icon-pic">
<p><a
th:href="${'javascript:gotoService ("'+kind.link+' ", "
");')" class="linka" ><img src="/images/home/BigIconFirm. png" /</a></p>
</div>
<div class="icon-txt">
<dl>
<dt><p><a
th:hrefe"'javascript:gotoService (1''+$(kind.link}+'\',N' ');'" class="linka"th:text="${kind.name}"></a></p>
<span><img
srC=" /images/home/FourStar.jpg"/></span>
</dt></dl></div></div>

It's used here Thymeleaf A loop statement of th:each, Display each record contained in the classification list on the page . Each record contains an image and a text link . among , Access to each link , Use gotoService Function to jump to the page .

After completing the above design , You can do a simple test .

start-up merchant-sso application , Enter the link shown below in the browser :http://localhost:8000

When I open the link , You can enter the login interface . Enter the user name and password generated in the previous unit test on the login interface , namely “admin/123456”. After successful login , You can open SSO The home page of , Pictured 10-6 Shown .

Be careful : chart 10-6 List of apps shown in , Will be determined by the permissions the user has . Suppose we assign more permissions to a user , Then the user will get more access to resources , Pictured 10-7 Shown .

In this article, we will explain the content of business management background and sso Design : SSO Design

  1. The next article will explain the background of business management and sso Design :SSO Client design ;
  2. Friends who think the article is good can forward this article and pay attention to Xiaobian ;
  3. Thank you for your support !
原网站

版权声明
本文为[Seconds to write code]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202170447305203.html