CAS5.3 server configuration, link mysql, custom password encryption, login page, login verification, custom exception, ajax login, etc

Posted by sdlyr8 on Tue, 04 Jan 2022 14:32:06 +0100

catalogue

1. Configuring mysql for cas server
2. cas server custom password encryption method
3. The cas server can customize the theme, that is, the login page, or other pages
4. The cas server adds fields to the form submitted during login
5. cas server custom login verification
6. cas server custom return exception
7. The cas server uses ajax to log in

Environmental Science:

CAS version: 5.3

Download link: git address is: https://github.com/apereo/cas-overlay-template

tomcat: 8

jdk: 1.8

1. First copy the source code of the cas server as follows:

1) Open with idea

In the red box is the code compiled after tomcat is configured through idea.

2) create the src directory as follows, and put the target/cas/META-INF file, target/cas/services and target / CAS / application Properties, copy these three files to resources.

Since CAS supports https by default, if you want to change to support http and obtain some basic configurations of other CAS servers, you can see this article: SSO single sign on (I) create cas server I'm a CSDN blog in mixed IT circle _casserver

1. Configuring mysql for cas server

 1,pom.xml

<!--Database authentication related start-->
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-support-jdbc</artifactId>
            <version>${cas.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-support-jdbc-drivers</artifactId>
            <version>${cas.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>
        <!--Database authentication related end-->

2,application.properties configuration file

1) Add a link to mysql

cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver
cas.authn.jdbc.query[0].url=jdbc:mysql://127.0.0.1:3306/shiro?useUnicode=true&characterEncoding=utf-8
cas.authn.jdbc.query[0].user=root #Database account
cas.authn.jdbc.query[0].password=123456  #Database password
cas.authn.jdbc.query[0].sql=select password from system_admin_user where account = ?  #Table query sql
cas.authn.jdbc.query[0].fieldPassword=password  #Fields in the table that belong to passwords

2) Comment out the default account password of cas, otherwise the account password can also be logged in

#cas.authn.accept.users=casuser::Mellon

Note: mysql is used for login here, and the password is not encrypted. For example, if the login password is 123456, the password of the database is 123456.

2. cas server custom password encryption method

1. Create class: MyPasswordEncoder

package com.example.cas.password;

import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * Password encryption
 */
public class MyPasswordEncoder implements PasswordEncoder {

    @Override
    public String encode(CharSequence charSequence) {
        System.out.println("encode===========Password entered by the user:" + charSequence);
        return charSequence.toString();
    }

    @Override
    public boolean matches(CharSequence charSequence, String str) {
        //charSequence is the password entered by the user
        System.out.println("matches---------------Password entered by the user:"+charSequence);
        //str is the database password
        System.out.println("matches---------------Database password:"+str);
        if (charSequence.equals(str)){
            return true;
        }
        return false;
    }
}

2,application.properties configuration file

#Turn on custom password authentication
cas.authn.jdbc.query[0].passwordEncoder.type=com.example.cas.MyPasswordEncoder

3. The cas server can customize the theme, that is, the login page, or other pages

1,pom.xml

<!--Custom login page start-->
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-core-webflow</artifactId>
            <version>${cas.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-core-webflow-api</artifactId>
            <version>${cas.version}</version>
        </dependency>
        <!--Custom login page end-->

2. Register json and create services / login-10000001 under resources json, the topic name is loginTheme

{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "^(https|http|imaps)://.*",
  "name" : "web",
  "id" : 10000001,
  "evaluationOrder" : 10,
  "accessStrategy" : {
    "@class" : "org.apereo.cas.services.DefaultRegisteredServiceAccessStrategy",
    "enabled" : true,
    "ssoEnabled" : true
  },
  "theme": "loginTheme"
}

3. Configuration file: application Properties enable json code scanning

# Service registry
# Open the JSON file. The default is false. Here is the way to open the JSON file in the services
cas.serviceRegistry.initFromJson=true
#Auto scan service configuration, enabled by default
cas.serviceRegistry.watcherEnabled=true
#Scan every 120 seconds
cas.serviceRegistry.schedule.repeatInterval=120000
#Delay 15 seconds on
# cas.serviceRegistry.schedule.startDelay=15000
cas.serviceRegistry.json.location=classpath:/services
#JSON file subject name loginTheme
cas.theme.defaultThemeName=loginTheme

4. Create logintheme under resources properties 

loginTheme.javascript.file=/themes/loginTheme/js/cas.js
loginTheme.standard.css.file=/themes/loginTheme/css/cas.css
loginTheme.login.images.path=/themes/loginTheme/images

cas.standard.css.file=/css/cas.css
cas.javascript.file=/js/cas.js
cas.admin.css.file=/css/admin.css

Create static resources and login files. The path is as follows:

# Static resource file
resources/static/themes/loginTheme/css/cas.css
resources/static/themes/loginTheme/js/cas.js
resources/static/themes/loginTheme/images/
# Login page
resources/templates/loginTheme/casLoginView.html

5,casLoginView.html: the name is fixed. You can't fool around. The page content can be your own. The following page is a copy of the cas login page.

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" th:src="@{/themes/customTheme/js/cas.js}"></script>
    <script type="text/javascript" th:src="@{/themes/customTheme/js/jquery.min.js}"></script>
</head>
<body>
<span>Login page</span><br>
<form method="post" id="fm1" th:object="${credential}" action="login">
    <div class="alert alert-danger" th:if="${#fields.hasErrors('*')}">
        <span th:each="err : ${#fields.errors('*')}" th:utext="${err}">Example error</span>
    </div>

    <h3 th:utext="#{screen.welcome.instructions}">Enter your Username and Password</h3>

    <section class="form-group">
        <label for="username" th:utext="#{screen.welcome.label.netid}">Username</label>

        <div th:if="${openIdLocalId}">
            <strong>
                <span th:utext="${openIdLocalId}"/>
            </strong>
            <input type="hidden"
                   id="username"
                   name="username"
                   th:value="${openIdLocalId}"/>
        </div>
        <div th:unless="${openIdLocalId}">
            <input class="form-control required"
                   id="username"
                   size="25"
                   tabindex="1"
                   type="text"
                   th:disabled="${guaEnabled}"
                   th:field="*{username}"
                   th:accesskey="#{screen.welcome.label.netid.accesskey}"
                   autocomplete="off"/>
        </div>
    </section>

    <section class="form-group">
        <label for="password" th:utext="#{screen.welcome.label.password}">Password</label>

        <div>
            <input class="form-control required"
                   type="password"
                   id="password"
                   size="25"
                   tabindex="2"
                   th:accesskey="#{screen.welcome.label.password.accesskey}"
                   th:field="*{password}"
                   autocomplete="off"/>
            <span id="capslock-on" style="display:none;">
                                <p>
                                    <i class="fa fa-exclamation-circle"></i>
                                    <span th:utext="#{screen.capslock.on}"/>
                                </p>
                </span>
        </div>
    </section>

    <section class="form-group">
        <label for="telephone">Telephone</label>

        <div th:if="${openIdLocalId}">
            <strong>
                <span th:utext="${openIdLocalId}"/>
            </strong>
            <input type="hidden"
                   id="telephone"
                   name="username"
                   th:value="${openIdLocalId}"/>
        </div>
        <div th:unless="${openIdLocalId}">
            <input class="form-control required"
                   id="telephone"
                   size="25"
                   tabindex="1"
                   type="text"
                   th:disabled="${guaEnabled}"
                   th:field="*{telephone}"
                   th:accesskey="#{screen.welcome.label.netid.accesskey}"
                   autocomplete="off"/>
        </div>
    </section>

    <section class="form-check" th:if="${passwordManagementEnabled && param.doChangePassword != null}">
        <p>
            <input type="checkbox" name="doChangePassword" id="doChangePassword"
                   value="true" th:checked="${param.doChangePassword != null}" tabindex="4"/>
            <label for="doChangePassword" th:text="#{screen.button.changePassword}">Change Password</label>
        </p>
    </section>

    <section class="form-check" th:if="${rememberMeAuthenticationEnabled}">
        <p>
            <input type="checkbox" name="rememberMe" id="rememberMe" value="true" tabindex="5"/>
            <label for="rememberMe" th:text="#{screen.rememberme.checkbox.title}">Remember Me</label>
        </p>
    </section>

    <section class="row"
             th:if="${recaptchaSiteKey != null AND recaptchaInvisible != null AND recaptchaSiteKey != null AND !recaptchaInvisible}">
        <div class="g-recaptcha" th:attr="data-sitekey=${recaptchaSiteKey}"/>
    </section>

    <input type="hidden" name="execution" th:value="${flowExecutionKey}"/>
    <input type="hidden" name="_eventId" value="submit"/>
    <input type="hidden" name="geolocation"/>
    <p th:if="${#request.getMethod().equalsIgnoreCase('POST')}">
                          <span th:each="entry : ${httpRequestInitialPostParameters}" th:remove="tag">
                              <span th:each="entryValue : ${entry.value}" th:remove="tag">
                                  <input type="hidden" th:name="${entry.key}" th:value="${entryValue}"/>
                              </span>
                          </span>
    </p>
    <input class="btn btn-block btn-submit"
           th:unless="${recaptchaSiteKey != null AND recaptchaInvisible != null AND recaptchaSiteKey != null AND recaptchaInvisible}"
           name="submit"
           accesskey="l"
           th:value="#{screen.welcome.button.login}"
           tabindex="6"
           type="submit"
           value="Login3"/>
    <button class="btn btn-block btn-submit g-recaptcha"
            th:if="${recaptchaSiteKey != null AND recaptchaInvisible != null AND recaptchaSiteKey != null AND recaptchaInvisible}"
            th:attr="data-sitekey=${recaptchaSiteKey}, data-badge=${recaptchaPosition}"
            data-callback="onSubmit"
            name="submitBtn"
            accesskey="l"
            th:text="#{screen.welcome.button.login}"
            tabindex="6"/>
</form>
</body>
</html>

4. The cas server adds fields to the form submitted during login

Add fields to the login form

1. Create a class that accepts new fields in the form: CustomCredential

package com.example.cas.login;

import org.apereo.cas.authentication.UsernamePasswordCredential;

public class CustomCredential extends UsernamePasswordCredential {

    private String telephone;

    public String getTelephone() {
        return telephone;
    }

    public void setTelephone(String telephone) {
        this.telephone = telephone;
    }
}

2. Add custom field class: CustomWebflowConfigurer

package com.example.cas.config;

import com.example.cas.login.CustomCredential;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.web.flow.CasWebflowConstants;
import org.apereo.cas.web.flow.configurer.AbstractCasWebflowConfigurer;
import org.springframework.context.ApplicationContext;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.ViewState;
import org.springframework.webflow.engine.builder.BinderConfiguration;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;

/**
 * Add custom field
 * @author anumbrella
 */
public class CustomWebflowConfigurer extends AbstractCasWebflowConfigurer {


    public CustomWebflowConfigurer(FlowBuilderServices flowBuilderServices,
                                   FlowDefinitionRegistry flowDefinitionRegistry,
                                   ApplicationContext applicationContext,
                                   CasConfigurationProperties casProperties) {
        super(flowBuilderServices, flowDefinitionRegistry, applicationContext, casProperties);
    }

    @Override
    protected void doInitialize() {
        final Flow flow = super.getLoginFlow();
        bindCredential(flow);
    }

    /**
     * Bind custom Credential information
     * @param flow
     */
    protected void bindCredential(Flow flow) {
        // Override binding custom credential
        createFlowVariable(flow, CasWebflowConstants.VAR_ID_CREDENTIAL, CustomCredential.class);
        //Bind new parameters to login page
        final ViewState state = (ViewState) flow.getState(CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM);
        final BinderConfiguration cfg = getViewStateBinderConfiguration(state);
        // Since the user name and password have been bound, you only need to bind the newly added system parameters
        // Field name, converter, whether the field must be telephone: it is a newly added field, and multiple fields can be added
        cfg.addBinding(new BinderConfiguration.Binding("telephone", null, true));
        cfg.addBinding(new BinderConfiguration.Binding("phone", null, true));
    }
}

3. Add custom field class to configuration: CustomerAuthWebflowConfiguration

package com.example.cas.config;

import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.web.flow.CasWebflowConfigurer;
import org.apereo.cas.web.flow.CasWebflowExecutionPlan;
import org.apereo.cas.web.flow.CasWebflowExecutionPlanConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;

/**
 * Configure add custom field class
 */
@Configuration("customerAuthWebflowConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
public class CustomerAuthWebflowConfiguration implements CasWebflowExecutionPlanConfigurer {

    @Autowired
    private CasConfigurationProperties casProperties;

    @Autowired
    @Qualifier("loginFlowRegistry")
    private FlowDefinitionRegistry loginFlowDefinitionRegistry;

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private FlowBuilderServices flowBuilderServices;

    @Bean
    public CasWebflowConfigurer customWebflowConfigurer() {
        //Instantiate a custom form configuration class
        final CustomWebflowConfigurer c = new CustomWebflowConfigurer(flowBuilderServices, loginFlowDefinitionRegistry,
                applicationContext, casProperties);
        // initialization
        c.initialize();
        // Return object
        return c;
    }

    @Override
    public void configureWebflowExecutionPlan(final CasWebflowExecutionPlan plan) {
        plan.registerWebflowConfigurer(customWebflowConfigurer());
    }
}

4. In document resources / meta-inf / spring In factories, add configuration:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.cas.config.CustomerAuthWebflowConfiguration

5. cas server custom login verification

1,pom.xml

<!--Custom login verification start-->
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-support-json-service-registry</artifactId>
            <version>${cas.version}</version>
        </dependency>
        <!-- Custom Authentication -->
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-core-authentication-api</artifactId>
            <version>${cas.version}</version>
        </dependency>
        <!-- Custom Configuration -->
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-core-configuration-api</artifactId>
            <version>${cas.version}</version>
        </dependency>
        <!--Custom login verification end-->

2. Custom verification class: CustomUsernamePasswordAuthentication

package com.example.cas.login;

import com.example.cas.Model.UserInfo;
import com.example.cas.Model.CustomCredential;
import org.apereo.cas.authentication.*;
import org.apereo.cas.authentication.handler.support.AbstractPreAndPostProcessingAuthenticationHandler;
import org.apereo.cas.authentication.principal.PrincipalFactory;
import org.apereo.cas.services.ServicesManager;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import javax.security.auth.login.AccountLockedException;
import javax.security.auth.login.FailedLoginException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;


/**
 * Custom login verification
 */
public class CustomUsernamePasswordAuthentication extends AbstractPreAndPostProcessingAuthenticationHandler {


    public CustomUsernamePasswordAuthentication(String name, ServicesManager servicesManager, PrincipalFactory principalFactory, Integer order) {
        super(name, servicesManager, principalFactory, order);
    }

    @Override
    public boolean supports(Credential credential) {
        //Judge whether the passed Credential is a type that you can handle
        return credential instanceof CustomCredential;
    }

    /**
     * Authentication, that is, login
     * @param credential
     * @return
     * @throws GeneralSecurityException
     * @throws PreventedException
     */
    @Override
    protected AuthenticationHandlerExecutionResult doAuthentication(Credential credential) throws GeneralSecurityException, PreventedException {
        System.out.println("++========================="+4);
        UsernamePasswordCredential usernamePasswordCredential = (UsernamePasswordCredential) credential;
        String username = usernamePasswordCredential.getUsername();
        String password = usernamePasswordCredential.getPassword();
        System.out.println("******CustomUsernamePasswordAuthentication******: " + username);
        System.out.println("******CustomUsernamePasswordAuthentication******: " + password);

        /*Query database data through jdbc*/
        // JDBC template depends on connection pool to obtain data connection, so connection pool must be constructed first
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/shiro?useUnicode=true&characterEncoding=utf-8");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        // Create JDBC template
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        String sql = "SELECT * FROM system_admin_user WHERE account = ?";
        UserInfo info = (UserInfo) jdbcTemplate.queryForObject(sql, new Object[]{username}, new BeanPropertyRowMapper(UserInfo.class));

        System.out.println("database username : "+ info.getAccount());
        System.out.println("database password : "+ info.getPassword());

        //The abnormality here is not good. It won't work
        if (info != null) {
            throw new AccountLockedException();
        }
        if (!info.getPassword().equals(password)) {
            throw new FailedLoginException("Sorry, Incorrect password!");
        } else {
            //Multiple attribute information returned to the client can be customized
            HashMap<String, Object> returnInfo = new HashMap<>();
            returnInfo.put("id", info.getId());
            returnInfo.put("account", info.getAccount());
            returnInfo.put("disableStatus", info.getDisableStatus());
            final List<MessageDescriptor> list = new ArrayList<>();
            return createHandlerResult(usernamePasswordCredential, this.principalFactory.createPrincipal(username, returnInfo), list);
        }
    }

}

3. Injection configuration: CustomAuthenticationConfiguration

package com.example.cas.login;

import org.apereo.cas.authentication.AuthenticationEventExecutionPlan;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlanConfigurer;
import org.apereo.cas.authentication.AuthenticationHandler;
import org.apereo.cas.authentication.principal.DefaultPrincipalFactory;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.services.ServicesManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Injection configuration information
 */
@Configuration("CustomAuthenticationConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
public class CustomAuthenticationConfiguration implements AuthenticationEventExecutionPlanConfigurer {
    @Autowired
    private ServicesManager servicesManager;

    @Bean
    public AuthenticationHandler myAuthenticationHandler() {
        // Parameters: name, servicesManager, principalFactory, order
        // Defined as the priority to use it for authentication
        return new CustomUsernamePasswordAuthentication(CustomUsernamePasswordAuthentication.class.getName(),
                servicesManager, new DefaultPrincipalFactory(), 1);
    }

    @Override
    public void configureAuthenticationExecutionPlan(final AuthenticationEventExecutionPlan plan) {
        plan.registerAuthenticationHandler(myAuthenticationHandler());
    }
}

4. In document resources / meta-inf / spring In factories, add configuration:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.cas.login.CustomAuthenticationConfiguration

Note: the exception return in the above custom login is not allowed.

6. cas server custom return exception
7. The cas server uses ajax to log in

There is no time for these two directories.

Topics: Database MySQL security cas